周五在公司搭好的ELK上查看日志,組長讓看看其中NullPointerException出現很多的原因。
通過NullPointerException搜索,點看其中一個查看,發現異常的信息就一行java.lang.NullPointerException,並沒有堆棧信息。
看了幾個都沒有,然后翻代碼看記日志的地方,調用了日志基礎工程的一個方法,異常對象通過封裝傳了進去,其中異常信息屬性取值用的是Throwables.getStackTraceAsString(e),這是guava包提供的方法,點進去看是取了所有堆棧信息的。
那么問題來了,為什么堆棧信息沒顯示出來呢?
百度了下,關鍵字:NullPointerException 沒有堆棧
找到類似的問題,發現是JVM虛擬機對異常信息進行了優化,當相同異常出現很多次,會認為它是熱點異常,忽略掉異常堆棧信息;通過增加JVM參數:-XX:-OmitStackTraceInFastThrow可解決。
而項目工程里那個異常,產生來源是一個storm計算過程,它每天會進行很多次,因此很可能就是這個原因。
於是修改storm啟動腳本,增加此JVM參數:-XX:-OmitStackTraceInFastThrow。重新提交topology到storm,很快能查看到最新的NullPointerException日志,看異常堆棧信息已完整輸出,能夠定位到具體代碼行了。
本機寫了個簡單程序測試,循環很多次調用產生NullPointerException的方法,只打印最后一次異常:
1 package com.cdfive.learn.guava; 2 3 import com.google.common.base.Throwables; 4 5 /** 6 * 堆棧信息丟失測試 7 * -XX:-OmitStackTraceInFastThrow 8 * @author five 9 * @date 2018-05-27 10 */ 11 public class ThrowablesTest { 12 public static void main(String[] args) { 13 int max = 100; 14 // int max = 10000;// jvm參數增加-XX:-OmitStackTraceInFastThrow,否則只輸出java.lang.NullPointerException,沒有堆棧信息 15 for (int i = 1; i <= max; i++) { 16 try { 17 npeMethod(); 18 } catch (NullPointerException e) { 19 if (i == max) { 20 System.out.println(Throwables.getStackTraceAsString(e)); 21 } 22 } 23 } 24 } 25 26 public static void npeMethod() { 27 String s = null; 28 s = s.substring(0); 29 } 30 }
當max=100時,數量較少,能輸出完整的異常堆棧:
java.lang.NullPointerException
at com.cdfive.learn.guava.ThrowablesTest.npeMethod(ThrowablesTest.java:28)
at com.cdfive.learn.guava.ThrowablesTest.main(ThrowablesTest.java:17)
當 max=10000時,輸出結果中堆棧丟失了:
java.lang.NullPointerException
在JVM啟動參數中增加:-XX:-OmitStackTraceInFastThrow后,又能夠輸出完整的異常堆棧了。
-----------------------------------------------------------------------------------------------------------------------------------
參考:
JVM參數分享 OmitStackTraceInFastThrow https://www.jianshu.com/p/e87d166380eb
https://blog.csdn.net/shfqbluestone/article/details/70978852
https://blog.csdn.net/taotao4/article/details/43918131