问题

系统上线时,查看问题日志时,有时候发现日志捕获了NullPointerException,但是缺没有任何的异常堆栈信息。只有一条信息:java.lang.NullPointerException 不清楚问题原因。

原因

HostSpot VM对异常进行了优化:fast throw。如果检测到代码中特定位置的异常抛出次数太多的话,就会决定用fast throw来对异常进行优化—直接抛出一个事先分配好的、类型匹配的异常对象。它的message和stack trace都是清空的。

引用R大的一段话: HotSpot VM有个许多人觉得“匪夷所思”的优化,叫做fast throw:有些特定的隐式异常类型(NullPointerException、ArithmeticException( / 0)之类)如果在代码里某个特定位置被抛出过多次的话,HotSpot Server Compiler(C2)会透明的决定用fast throw来优化这个抛出异常的地方——直接抛出一个事先分配好的、类型匹配的异常对象。这个对象的message和stack trace都被清空。抛出这个异常的速度是非常快,不但不用额外分配内存,而且也不用爬栈;但反面就是可能正好是需要知道哪里出问题的时候看不到stack trace了。从Sun JDK5开始要避免C2做这个优化还得额外传个VM参数:-XX:-OmitStackTraceInFastThrow。

复现

大量操作可以复现下。

  1. public static void main(String[] args) {
  2. for (int i = 0; i < 1000000; i++) {
  3. try {
  4. t();
  5. } catch (Exception e) {
  6. e.printStackTrace();
  7. }
  8. }
  9. }
  10. public static void t(){
  11. ((Object)(null)).getClass();
  12. }

运行一段时间后,出现如下:

  1. java.lang.NullPointerException
  2. java.lang.NullPointerException
  3. java.lang.NullPointerException
  4. java.lang.NullPointerException
  5. java.lang.NullPointerException
  6. java.lang.NullPointerException
  7. java.lang.NullPointerException

解决

添加vm参数:-XX:-OmitStackTraceInFastThrow 即可解决。但是一般不建议添加,大量报错了,还是优先考虑去解决这个错误,而不是让它继续报错。

参考