Java NullPointerException 出错栈只有一行

通常情况下, 一个新异常(Throwable, Exception 以及其子类)在创建的时候, 都会把当前线程的所有栈放到 Throwable 的 stackTrace 字段上面. 所以, 当我们打印出错栈的时候, 能完全看到该线程是在那一行出错的, 怎么一层层运行到这行代码的. 可是有时候, 我们却发现打印的栈里面只有一行, 没有整个出错栈. 例子如下:

java.lang.NullPointerException

当然正常情况下, 绝不可能只有一样, 即使从 main 函数运行, 或者整个出错栈在一个 Thread 或 Runnable 代码里面, 也不能只有这么一行出错栈. 那么问题出在哪呢?

host:~ admin$ java  -XX:+PrintFlagsFinal -version | grep OmitStackTraceInFastThrow.
     bool OmitStackTraceInFastThrow                 = true             {product}
openjdk version "1.8.0_221"

基于 Hotspot 的 JVM 有一个flag: OmitStackTraceInFastThrow, 默认它是打开的. 它的意思是: 对于一些 JVM 自带的 Exception, 当很少被创建的时候, 他们都是把全部 stacktrace 都放进去的. 当一个异常经常被创建的时候, 这部分代码就会被执行多次, 超过 JIT 编译的 threshold, 就会被 JIT 编译器编译. 编译的时候, 为了性能考虑, JVM 会把出错栈部分省略成只剩一行, 那么就是我们看到的样子.

原文解释:

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.

所以, 如果出了这种问题, 找不到出错栈, 就去找最早的错误, 里面是有出错栈的. 等到编译之后的代码去运行, 出错栈就被省略.

如果要强制它即使编译之后, 还有出错栈, 那么就需要在 JVM 启动时候, 加上 -XX:+OmitStackTraceInFastThrow 参数.

参考:

  1. https://www.oracle.com/java/technologies/javase/release-notes-introduction.html

标签: none

添加新评论