我的 Java 代码实例捕获了 NullPointerException
,但是当我尝试记录 StackTrace(基本上最终调用 Throwable.printStackTrace()
)时,我得到的只是:
java.lang.NullPointerException
有没有其他人遇到过这个?我尝试用谷歌搜索“java 空指针空堆栈跟踪”,但没有遇到这样的事情。
-XX:-OmitStackTraceInFastThrow
的更多信息:stackoverflow.com/questions/4659151/…
您可能正在使用 HotSpot JVM(最初由 Sun Microsystems,后来被 Oracle 收购,是 OpenJDK 的一部分),它执行了大量优化。要取回堆栈跟踪,您需要将选项 -XX:-OmitStackTraceInFastThrow
传递给 JVM。
优化是,当第一次发生异常(通常是 NullPointerException)时,会打印完整的堆栈跟踪,并且 JVM 会记住堆栈跟踪(或者可能只是代码的位置)。当该异常经常发生时,将不再打印堆栈跟踪,以实现更好的性能并且不使用相同的堆栈跟踪淹没日志。
要查看这是如何在 HotSpot JVM 中实现的,请grab a copy of it 并搜索全局变量 OmitStackTraceInFastThrow
。上次我查看代码时(2019 年),它位于文件 graphKit.cpp 中。
正如您在评论中提到的,您正在使用 log4j。我(无意中)发现了一个我写过的地方
LOG.error(exc);
而不是典型的
LOG.error("Some informative message", e);
由于懒惰,或者只是没有考虑它。不幸的是,它的行为不像您预期的那样。 logger API 实际上将 Object 作为第一个参数,而不是字符串 - 然后它在参数上调用 toString()。因此,它没有得到漂亮漂亮的堆栈跟踪,而是打印出 toString ——这在 NPE 的情况下是毫无用处的。
也许这就是你正在经历的?
我们过去也看到过同样的行为。事实证明,出于某种疯狂的原因,如果 NullPointerException 在代码中的同一位置多次发生,过一段时间使用 Log.error(String, Throwable)
将停止包含完整的堆栈跟踪。
尝试进一步查看您的日志。你可能会找到罪魁祸首。
编辑: this bug 听起来很相关,但它在很久以前就已修复,可能不是原因。
-XX:-OmitStackTraceInFastThrow
JVM 标志?另见stackoverflow.com/a/2070568/6198。
这是一个解释:Hotspot caused exceptions to lose their stack traces in production – and the fix
我已经在 Mac OS X 上测试过了
java版本“1.6.0_26”
Java(TM) SE 运行时环境 (build 1.6.0_26-b03-383-11A511)
Java HotSpot(TM) 64 位服务器 VM(内部版本 20.1-b02-383,混合模式)对象字符串 = “abcd”;诠释 i = 0;而 (i < 12289) { i++;尝试{整数a =(整数)字符串; } 捕捉(异常 e){ e.printStackTrace(); } }
对于这个特定的代码片段,12288 次迭代(+频率?)似乎是 JVM 决定使用预分配异常的限制......
exception.toString
不给您 StackTrace,它只返回
这个 throwable 的简短描述。结果是以下内容的连接: * 此对象的类名 * ":"(冒号和空格) * 调用此对象的 getLocalizedMessage() 方法的结果
请改用 exception.printStackTrace
来输出 StackTrace。
getStackTrace()
来确保问题不在于您的记录器?
替代建议 - 如果您使用的是 Eclipse,您可以在 NullPointerException 本身上设置一个断点(在 Debug 透视图中,转到“Breakpoints”选项卡并单击其中包含 ! 的小图标)
检查“已捕获”和“未捕获”选项 - 现在当您触发 NPE 时,您将立即断点,然后您可以逐步查看它是如何处理的以及为什么您没有获得堆栈跟踪。
toString()
仅返回异常名称和可选消息。我建议打电话
exception.printStackTrace()
转储消息,或者如果您需要血淋淋的详细信息:
StackTraceElement[] trace = exception.getStackTrace()
这将输出异常,仅用于调试您应该更好地处理异常。
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();
}
(您的问题仍然不清楚您的代码是调用 printStackTrace()
还是由日志处理程序完成。)
以下是关于可能发生的事情的一些可能解释:
正在使用的记录器/处理程序已配置为仅输出异常的消息字符串,而不是完整的堆栈跟踪。
您的应用程序(或某些第三方库)正在使用 LOG.error(ex); 记录异常;而不是(例如)log4j Logger 方法的 2 参数形式。
消息来自与您认为的不同的地方;例如,它实际上是来自一些第三方库方法,或者是早期尝试调试时遗留下来的一些随机东西。
正在记录的异常重载了一些方法来掩盖堆栈跟踪。如果是这种情况,异常将不是真正的 NullPointerException,而是 NPE 的一些自定义子类型,甚至是一些未连接的异常。
我认为最后一种可能的解释不太可能,但人们至少确实考虑过做这种事情来“防止”逆向工程。当然,它只会真正成功地让诚实的开发人员感到困难。
当您在项目中使用 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)
-XX:-OmitStackTraceInFastThrow
标志。我还没有确认这是否是我也未能打印堆栈跟踪的原因(例如,使用e.printStackTrace
),但这似乎很有可能。我已经扩展了答案以反映这一发现。