java lost exception stack traces

tomcat运行时报错,但查看日志时,只能看到一句NPE,却没有相关的异常栈信息。

java.lang.NullPointerException
java.lang.NullPointerException
java.lang.NullPointerException
java.lang.NullPointerException

示例代码

public class NullPointerExceptionMissingStack {    public static void main(String[] args) {        int i = 0;        int j = 0;        String x = null;        while (i++ < 1_000_000) {            try {                throwNPE(x);            } catch (Exception e) {                int length = e.getStackTrace().length;                e.printStackTrace();                if (length == 0) {                    j++;                    if (j > 100) {                        System.out.println(i);                        System.out.println(j);                        return;                    }                }            }        }    }    private static void throwNPE(String x) {        x.toString();    }}

异常抛出次数到达一定程度时,这里jvm会进行优化,只保留了固定的错误,却不再保留异常堆栈,打印内容部分如下:

java.lang.NullPointerException    at com.example.test.lang.NullPointerExceptionMissingStack.throwNPE(NullPointerExceptionMissingStack.java:63)    at com.example.test.lang.NullPointerExceptionMissingStack.main(NullPointerExceptionMissingStack.java:45)java.lang.NullPointerException    at com.example.test.lang.NullPointerExceptionMissingStack.throwNPE(NullPointerExceptionMissingStack.java:63)    at com.example.test.lang.NullPointerExceptionMissingStack.main(NullPointerExceptionMissingStack.java:45)java.lang.NullPointerException    at com.example.test.lang.NullPointerExceptionMissingStack.throwNPE(NullPointerExceptionMissingStack.java:63)    at com.example.test.lang.NullPointerExceptionMissingStack.main(NullPointerExceptionMissingStack.java:45)java.lang.NullPointerException    at com.example.test.lang.NullPointerExceptionMissingStack.throwNPE(NullPointerExceptionMissingStack.java:63)    at com.example.test.lang.NullPointerExceptionMissingStack.main(NullPointerExceptionMissingStack.java:45)java.lang.NullPointerExceptionjava.lang.NullPointerExceptionjava.lang.NullPointerExceptionjava.lang.NullPointerExceptionjava.lang.NullPointerExceptionjava.lang.NullPointerExceptionjava.lang.NullPointerExceptionjava.lang.NullPointerExceptionjava.lang.NullPointerException

oracle的官方说明

The compiler in the server VM now provides correct stack backtraces for all "cold" built-in exceptions.
For performance purposes, when such an exception is thrown a few times, the method may be recompiled.
After recompilation, the compiler may choose a faster tactic using preallocated exceptions that do not provide a stack trace.
To disable completely the use of preallocated exceptions, use this new flag: -XX:-OmitStackTraceInFastThrow.

再将之前tomcat的日志往前查找,的确有完整的异常栈信息。

References

  1. Hotspot caused exceptions to lose their stack traces in production – and the fix
  2. NullPointerException in Java with no StackTrace
  3. NullPointerException丢失异常堆栈信息