案例分享-Exception.getMessage突然爲null

 

背景

之前做的小工具一個jsqlparse+git做的小工具幫我節省時間摸魚昨天突然停止工作,看了下jvm並沒有退出,但是看日誌確實有不少Error輸出,雖說是一個普通的NPE,但是分析了一下卻疑點重重,所以花點時間來一探究竟,最終又掌握一個jvm知識點,還是比較有意思。

錯誤現場

以下是示例代碼,爲了說明問題做了簡化,大概意思是使用CCJSqlParserUtil去解析一段sql語句,如果解析出錯了以後從JSQLParserException.getMessage()中利用正則提取出具體的行和列。

Statements statements = null;
Set<Integer> sqlSet = new HashSet<>();
String sql ="alter table test add column varchra(4)";
try {
    statements = CCJSqlParserUtil.parseStatements(sql);
} catch (JSQLParserException e) {
        Pattern pattern = Pattern.compile("line (\\d+), column (\\d+)");
        String message = e.getMessage();
        Matcher m = pattern.matcher(message);
        int line = -1;
        int column = -1;
        while(m.find()){
            int groupCount = m.groupCount();
            if(groupCount > 0){
                line = Integer.parseInt(m.group(1));
                column = Integer.parseInt(m.group(2));
                break;
            }
        }
}

上面那個錯誤sql解析出錯了以後的異常信息如下:

Encountered unexpected token: "varchra" <S_IDENTIFIER>
    at line 1, column 29.

Was expecting:

    "COMMENT"

那個詭異的NPE 棧如下:

java.lang.NullPointerException: null
        at java.util.regex.Matcher.getTextLength(Matcher.java:1283)
        at java.util.regex.Matcher.reset(Matcher.java:309)
        at java.util.regex.Matcher.<init>(Matcher.java:229)
        at java.util.regex.Pattern.matcher(Pattern.java:1093)
        at xxx.ScriptUtil.sqlParse(ScriptUtil.java:41)

很顯然是e.getMessage()返回了null導致pattern.matcher(message)失敗,但是e.getMessage()理論上來講不會是null,有點玄學的味道,一般解決玄學的首要方法是重啓大法(個人觀點,歡迎來噴,哈哈)。果然,重啓了以後竟然好了,好奇心一下就被激發了。

錯誤原因

網上一通搜索確實類似的案例不少,大概的意思是jvm對異常處理這塊做了優化,如果頻繁拋出某種異常jvm會對這些異常做一些處理,使用JVM初始化的時候創建的那些異常對象來替代本應該新建的異常對象,因此這些異常棧和Message是空的,這一特性受OmitStackTraceInFastThrow參數的管控,可以通過-XX:+OmitStackTraceInFastThrow開啓,或者-XX:-OmitStackTraceInFastThrow關閉,看完確實恍然大悟,但是並沒有找到官方的一些說明,還是心有不甘,決定在openjdk源碼中找找答案,全局在openjdk8的源碼中搜索OmitStackTraceInFastThrow關鍵字,確實得到了想要的答案,一起來看下。

 結合網上的一些結論和源碼來看只有以下幾類異常纔會觸發OmitStackTraceInFastThrow,分別是NullPointerException、ArithmeticException、ArrayIndexOutOfBoundsException、ArrayStoreException、ClassCastException,最終發現是有一個腳本文件的內容爲空,會觸發jsqlparse發生ArrayIndexOutOfBoundsException,進而觸發了OmitStackTraceInFastThrow特性,導致工具代碼中e.getMessage()返回null而觸發NPE造成工具停止運行的假象。

修復辦法

  1. 使用-XX:-OmitStackTraceInFastThrow關閉這一特性;

  2. 對執行邏輯優化,如果發現腳本文件內容爲空就直接返回,不再繼續執行;

推薦閱讀

https://opts.console.heapdump.cn/result/query/Ex13k

https://heapdump.cn/topic/OmitStackTraceInFastThrow

一個jsqlparse+git做的小工具幫我節省時間摸魚

  

 

  

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章