程序中抛出了大量的NullPointerException,但是奇怪的是没有堆栈信息,明明是堆栈打印到日志中的。

后来发现在大量的NullPointerException日志中,有小部分是有堆栈信息的,大部分没有。原来是Hotspot JVM在1.5时添加了一项优化OmitStackTraceInFastThrow。如果同一个异常被抛出很多次,则这个方法会被重新编译,重新编译后,这个方法会使用更快的抛出异常的方式,也就是一个预先分配好的不带堆栈信息的异常。所以在函数被JIT编译前,能看到堆栈信息,编译后,就没有堆栈信息了。

下面的代码可以模拟这种情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
try {
work();
} catch (Exception e) {
e.printStackTrace();
}
}
}

private static void work(){
String a = null;
a.length();
}

一开始会抛出正常的带堆栈信息的异常:

1
2
3
java.lang.NullPointerException
at fastthrow.Main.work(Main.java:22)
at fastthrow.Main.main(Main.java:13)

一段时间后就没有堆栈信息了:

1
java.lang.NullPointerException

这个优化可以通过参数-XX:-OmitStackTraceInFastThrow关闭,这样就不会丢失堆栈了。

还有一个问题是什么异常会触发这个优化,所查资料没看到具体定义。大家举的例子都是NullPointerException。

我试着直接throw new NullPointerException()是不会触发优化的,throw自定义异常也不会。

“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.” http://java.sun.com/j2se/1.5.0/relnotes.html

从这段官方的原话中可以看出是针对“cold” built-in exceptions,但是具体什么是“cold” built-in exceptions还是不得而知。

参考资料