• 案例分享-Exception.getMessage突然为null


     

    背景

    之前做的小工具一个jsqlparse+git做的小工具帮我节省时间摸鱼昨天突然停止工作,看了下jvm并没有退出,但是看日志确实有不少Error输出,虽说是一个普通的NPE,但是分析了一下却疑点重重,所以花点时间来一探究竟,最终又掌握一个jvm知识点,还是比较有意思。

    错误现场

    以下是示例代码,为了说明问题做了简化,大概意思是使用CCJSqlParserUtil去解析一段sql语句,如果解析出错了以后从JSQLParserException.getMessage()中利用正则提取出具体的行和列。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Statements statements = null;
    Set 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解析出错了以后的异常信息如下:

    1
    2
    3
    4
    5
    6
    Encountered unexpected token: "varchra"
        at line 1, column 29.
     
    Was expecting:
     
        "COMMENT"

    那个诡异的NPE 栈如下:

    1
    2
    3
    4
    5
    6
    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.(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做的小工具帮我节省时间摸鱼

      

     

      

      

  • 相关阅读:
    conan入门(二十九):对阿里mnn进行Conan封装塈conans.CMake和conan.tools.cmake.CMake的区别
    复杂微纳结构制造需求旺盛 微纳3D打印市场发展前景广阔
    php的加密方式汇总
    车联网时代,能链车联凭什么成为“关键先生”?
    VisualStudio 编写C++项目常见问题解决
    《C Primer Plus》第12章复习题与编程练习
    RF和SVM的特点
    16. 文件上传
    同步 -- 互斥锁
    1024共码未来(一览中华风华,API First)
  • 原文地址:https://www.cnblogs.com/chopper-poet/p/17950273