没有 StackTrace 的 Java 中的 NullPointerException

Posted

技术标签:

【中文标题】没有 StackTrace 的 Java 中的 NullPointerException【英文标题】:NullPointerException in Java with no StackTrace 【发布时间】:2011-01-25 13:43:12 【问题描述】:

我的 Java 代码实例捕获了 NullPointerException,但是当我尝试记录 StackTrace(基本上最终调用 Throwable.printStackTrace())时,我得到的只是:

java.lang.NullPointerException

还有其他人遇到过这个吗?我尝试在谷歌上搜索“java 空指针空堆栈跟踪”,但没有遇到类似的情况。

【问题讨论】:

上下文是什么?是否涉及多个线程?我在尝试获取 SwingWorker 中异常的堆栈跟踪时遇到了问题。 这里不涉及线程,只是普通的旧 Java。 @Bozho - 不 - 还不确定如何重现 NullPointer。 相关:***.com/questions/1076191/… 更多关于-XX:-OmitStackTraceInFastThrow的信息:***.com/questions/4659151/… 【参考方案1】:

exception.toString 不会给你 StackTrace,它只会返回

这个 throwable 的简短描述。 结果是:

* the name of the class of this object
* ": " (a colon and a space)
* the result of invoking this object's getLocalizedMessage() method

使用exception.printStackTrace 代替输出 StackTrace。

【讨论】:

对不起,我在原来的帖子中说错了。我通过 Log4J 记录这些,它确实使用 printStackTrace()。 您是否尝试过使用getStackTrace() 来确保问题不在于您的记录器? 如果您使用 log4j,请务必将异常作为参数的一部分发送到 log 方法。我会发布一个答案。 @raviaw 有效点! @Edward Shtern:您能否确认您确实使用的是 log4j 方法的 2-arg 形式?我知道您在后面的回答中提到,这样做是公司的政策,但您绝对确定在这种情况下您遵守了政策吗? 这可能是一个很长的镜头,但异常是否可能源自某些第 3 方代码?也许它是一个(写得不好的)异常包装器,它的 toString() 只返回被包装的异常的类名,并且无法提供底层的堆栈跟踪。尝试将类似 logger.info("Exception class= " + exc.class.getCanonicalName()) 的内容放入你的 catch 块中,看看你会得到什么。【参考方案2】:

toString() 只返回异常名称和可选消息。我建议打电话给

exception.printStackTrace()

转储消息,或者如果您需要血淋淋的详细信息:

 StackTraceElement[] trace = exception.getStackTrace()

【讨论】:

见上文 - 我说错了 - 我正在使用 printStackTrace()。【参考方案3】:

这将输出异常,仅用于调试你应该更好地处理你的异常。

import java.io.PrintWriter;
import java.io.StringWriter;
    public static String getStackTrace(Throwable t)
    
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        t.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    

【讨论】:

【参考方案4】:

正如您在评论中提到的,您使用的是 log4j。我(无意中)发现了一个我写过的地方

LOG.error(exc);

而不是典型的

LOG.error("Some informative message", e);

由于懒惰,或者只是没有考虑它。不幸的是,它的行为不像您预期​​的那样。 logger API 实际上将 Object 作为第一个参数,而不是字符串 - 然后它在参数上调用 toString()。因此,它没有得到漂亮漂亮的堆栈跟踪,而是打印出 toString - 这在 NPE 的情况下是非常没用的。

也许这就是你正在经历的?

【讨论】:

+1:这可以解释所描述的行为,而且你不是唯一发现这个的人:) 我们实际上有一个标准策略,即从不使用上面的第一种形式 (LOG.error(exc);) - 我们总是使用 2 参数签名,以便我们在日志中添加一些描述性语句而不是只是一个原始的堆栈跟踪。 当然,但策略并不意味着它总是正确执行!认为至少值得一提。 是的,但在这种情况下是 ;-)【参考方案5】:

替代建议 - 如果您使用的是 Eclipse,您可以在 NullPointerException 本身上设置断点(在 Debug 透视图中,转到“Breakpoints”选项卡并单击其中包含 ! 的小图标)

检查“caught”和“uncaught”选项 - 现在,当您触发 NPE 时,您将立即断点,然后您可以逐步查看它是如何处理的,以及为什么您没有获得堆栈跟踪.

【讨论】:

【参考方案6】:

(您的问题仍然不清楚您的代码是调用printStackTrace() 还是由日志处理程序完成。)

以下是关于可能发生的情况的一些可能解释:

正在使用的记录器/处理程序已配置为仅输出异常的消息字符串,而不是完整的堆栈跟踪。

您的应用程序(或某些第三方库)正在使用 LOG.error(ex); 而不是(例如)log4j Logger 方法的 2 参数形式记录异常。

消息来自与您认为的不同的地方;例如它实际上来自一些第三方库方法,或者是早期尝试调试时遗留下来的一些随机东西。

正在记录的异常重载了一些方法来掩盖堆栈跟踪。如果是这种情况,异常将不是真正的 NullPointerException,而是 NPE 的一些自定义子类型,甚至是一些未连接的异常。

我认为最后一种可能的解释不太可能,但人们至少确实考虑过做这种事情来“防止”逆向工程。当然,它只会真正成功地让诚实的开发人员感到困难。

【讨论】:

【参考方案7】:

我们过去也看到过同样的行为。事实证明,由于某种疯狂的原因,如果 NullPointerException 在代码中的同一位置多次发生,一段时间后使用Log.error(String, Throwable) 将停止包含完整的堆栈跟踪。

尝试进一步查看您的日志。你可能会找到罪魁祸首。

编辑: this bug 听起来很相关,但它在很久以前就已修复,可能不是原因。

【讨论】:

bug 已关闭,但仍需要 -XX:-OmitStackTraceInFastThrow 标志来解决性能优化问题。 我最近经常看到这个。关于可能导致此问题或如何解决此问题的任何线索?日志系统可能已经运行了好几天,而真正的原因轮流出来了,别管繁琐的搜索...... Pawel,你试过 Joshua 建议的 -XX:-OmitStackTraceInFastThrow JVM 标志吗?另请参阅***.com/a/2070568/6198。 这是给我们的。谢谢。【参考方案8】:

您可能正在使用 HotSpot JVM(最初由 Sun Microsystems,后来被 Oracle 收购,是 OpenJDK 的一部分),它执行了大量优化。要取回堆栈跟踪,您需要将选项 -XX:-OmitStackTraceInFastThrow 传递给 JVM。

优化是当第一次发生异常(通常是 NullPointerException)时,会打印完整的堆栈跟踪,并且 JVM 会记住堆栈跟踪(或者可能只是代码的位置)。当该异常经常发生时,将不再打印堆栈跟踪,既可以实现更好的性能,也不会用相同的堆栈跟踪淹没日志。

要查看这是如何在 HotSpot JVM 中实现的,grab a copy of it 并搜索全局变量 OmitStackTraceInFastThrow。上次看代码时(2019年),在文件graphKit.cpp中。

【讨论】:

感谢您的提示。知道传递此选项是否有任何隐藏的陷阱(只要我的应用程序不抛出大量异常,这似乎很无害)? 我所知道的没有隐藏的陷阱。当你查看 Hotspot 源代码时,你可以看到这个选项只用在一个地方(graphKit.cpp)。我觉得这很好。 我想添加一点额外的信息,当堆栈跟踪被优化掉时,这是因为它至少被完全处理过一次:jawspeak.com/2010/05/26/… 我正在运行 OpenJDK JVM,版本 1.8.0u171 (Debian 9),它似乎也接受了 -XX:-OmitStackTraceInFastThrow 标志。我还没有确认这是否是我也未能打印堆栈跟踪的原因(例如,使用e.printStackTrace),但这似乎很有可能。我扩展了答案以反映这一发现。 在我们的例子中,前 125 个异常有一个堆栈跟踪,然后在 3 轮日志文件中其余的都没有。这个答案对找到罪魁祸首很有帮助。【参考方案9】:

这里有一个解释:Hotspot caused exceptions to lose their stack traces in production – and the fix

我已经在 Mac OS X 上测试过了

java版本“1.6.0_26” Java(TM) SE 运行时环境(内部版本 1.6.0_26-b03-383-11A511)

Java HotSpot(TM) 64 位服务器 VM(内部版本 20.1-b02-383,混合模式)

Object string = "abcd";
int i = 0;
while (i < 12289) 
    i++;
    try 
        Integer a = (Integer) string;
     catch (Exception e) 
        e.printStackTrace();
    

对于这个特定的代码片段,12288 次迭代(+频率?)似乎是 JVM 决定使用预分配异常的限制......

【讨论】:

【参考方案10】:

当您在项目中使用 AspectJ 时,可能会发生某些方面隐藏其堆栈跟踪部分。例如,今天我有:

java.lang.NullPointerException:
  at com.company.product.MyTest.test(MyTest.java:37)

此堆栈跟踪是在通过 Maven 的 surefire 运行测试时打印的。

另一方面,在 IntelliJ 中运行测试时,打印了不同的堆栈跟踪:

java.lang.NullPointerException
  at com.company.product.library.ArgumentChecker.nonNull(ArgumentChecker.java:67)
  at ...
  at com.company.product.aspects.CheckArgumentsAspect.wrap(CheckArgumentsAspect.java:82)
  at ...
  at com.company.product.MyTest.test(MyTest.java:37)

【讨论】:

以上是关于没有 StackTrace 的 Java 中的 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

Zap logger 是不是支持转义字符 '\n' 和 '\t' 来打印换行错误verbose 或 stacktrace

Stacktrace:] 根本原因是 java.lang.NullPointerException

java 递归StackTrace

java android stacktrace

java.langThrowable:STACKTRACE

Stacktrace: org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:5(