[译]生产环境中异常堆栈丢失的解决方案

今天我发现生产环境日志中有许多NullPointerException 没有堆栈。我们发现,当一些异常抛出的足够多时,JIT编译器会优化掉异常堆栈。下面的代码可以重现异常堆栈丢失的问题。我们的代码跑在jdk 1.6上(小版本号不记得了)

用下面的命令运行上面代码,异常丢失的情况就出现了(异常栈的长度从2变为了0):

javac NpeThief.java && java -classpath . NpeThief
javac NpeThief.java && java -server -classpath . NpeThief

如何解决呢?下面的参数设置可以修复这个问题,让NullPointerException异常堆栈的长度保持为2,不变为0:
javac NpeThief.java && java -XX:-OmitStackTraceInFastThrow -server -classpath . NpeThief
javac NpeThief.java && java -XX:-OmitStackTraceInFastThrow -classpath . NpeThief
javac NpeThief.java && java -Xint -classpath . NpeThief
javac NpeThief.java && java -Xint -server -classpath . NpeThief

可以看出,解决方案是:使用-XX:-OmitStackTraceInFastThrow参数让JIT不使用对异常堆栈的优化[1,2];或者设置-Xint让虚拟机以解释方式执行类的字节码,不将字节码编译为本机码[3]。那么-XX:-OmitStackTraceInFastThrow参数具体是什么意思呢?

JVM参数-XX:-OmitStackTraceInFastThrow参数可以关掉JVM对堆栈信息的优化。如果设置了这个参数,那么异常堆栈就能完整输出了。

“在服务器中的VM编译器现在提供准确的所有的“冷”内置异常堆栈回溯功能。为了性能考虑,当这些异常被抛出很多次时,这个方法会被重新编译,此后编译器将使用一种更快的抛出异常的方式,即抛出预先分配好的不带堆栈信息的异常。要完全关闭掉这种预分配的异常,就需要使用-XX:-OmitStackTraceInFastThrow参数。“http://java.sun.com/j2se/1.5.0/relnotes.html

请问设置这个参数后会影响性能吗?我确实使用这个方式java [args] -classpath . NpeThief做个了非常天真的测试。如我所期望的,只是编译慢了。但这解决了生产环境的问题吗?

答案是没有,我们需要修改生产环境JVM的启动参数,否则看到的log还是没有堆栈信息的。在我们修改了JVM启动参数,重新部署部署了以后,完整的异常堆栈信息出来了,问题解决了。

Reference

[1] Related sun bug 4292742 NullPointerException with no stack trace
[2] Helpful StackOverflow discussion of NullPointerException missing stack traces
[3] -Xint: Operate in interpreted-only mode. Compilation to native code is disabled, and all bytecodes are executed by the interpreter. The performance benefits offered by the Java HotSpot VMs’ adaptive compiler will not be present in this mode.

本文翻译自:Hotspot caused exceptions to lose their stack traces in production – and the fix

原创文章:[译]生产环境中异常堆栈丢失的解决方案,转载请注明:转载自戎码一生

Post Footer automatically generated by wp-posturl plugin for wordpress.



  1. 有些地方翻译错了。
    “如我所期望的,只是编译慢了。” -> "如我所期望的, 使用int参数的方案最慢"
    其他错误太多了,不一一指出了。
    翻译错误太误人子弟了。