問題與分析
最近在查項目的log時發現報了大量的NPE(NullPointerException),詭異的是只log了Exception的類名,卻沒有具體的堆棧信息,以致於無法對該NPE異常進行准確定位。
這是因為jvm自身存在着優化機制,但一個同樣的異常重復出現並被打印到log后,jvm可以不提供具體的堆棧信息來提高性能。關於這個的具體信息我們可以從官網上查到相關的資料:
http://www.oracle.com/technetwork/java/javase/relnotes-139183.html#vm
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.
谷歌翻譯如下:
服務器VM中的編譯器現在為所有“冷”內置異常提供正確的堆棧回溯。出於性能目的,當拋出這樣的異常幾次時,可以重新編譯該方法。重新編譯之后,編譯器可以使用不提供堆棧跟蹤的預分配異常來選擇更快的策略。要完全禁用預分配的異常,請使用以下新標志:-XX:-OmitStackTraceInFastThrow。
解決方案
有兩個解決方案,第一個是安裝官網說的,可以通過設置jvm的啟動參數來關閉該策略:
-XX:-OmitStackTraceInFastThrow
另一個解決方案是不設置啟動參數,直接重新啟動服務器,比如Tomcat。重啟服務器時jvm被重新啟動,這樣再遇到同樣的Exception時就會打印出來,當然如果后續如果重復遇到同樣的Exception還是無法打印出具體的異常棧信息。
當時我是選擇了后者這個方案,因為如果啟用了該參數會導致log日志太過龐大,也降低了性能,直接重啟服務器,並快速定位bug以便於解決問題。
補充
如果想了解更多關於該參數的細節,可以參考下邊的文章: