案例分享-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<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解析出错了以后的异常信息如下:
1 2 3 4 5 6 | Encountered unexpected token: "varchra" <S_IDENTIFIER> 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.<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造成工具停止运行的假象。
修复办法
-
使用-XX:-OmitStackTraceInFastThrow关闭这一特性;
-
对执行逻辑优化,如果发现脚本文件内容为空就直接返回,不再继续执行;
推荐阅读
https://opts.console.heapdump.cn/result/query/Ex13k
https://heapdump.cn/topic/OmitStackTraceInFastThrow
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?