为什么同JVM下第二次启动JFR录制会丢失自定义事件的部分字段?
问题根因
该问题是OpenJDK 11、16版本中JFR处理自定义事件动态字符串字段的已知缺陷:
第一次JFR录制结束后,JVM会为自定义事件的字段访问生成JIT优化快速路径,对于运行时动态生成的未入池字符串(你代码中span.name()返回值、字符串拼接得到的eventName2都属于这类),第二次录制启动时JFR不会自动将这些动态字符串注册到新录制的字符串常量池中。而字面量常量字符串(比如eventName3赋值的"test-test-test"在类加载时就已经进入全局字符串池)、基础类型字段(比如long类型的timestamp)不受该逻辑影响,所以可以正常显示。
修复方案
可任选以下一种方案解决:
- 对动态赋值的字符串字段主动调用
intern()方法,让动态字符串进入全局常量池,修改后的事件赋值代码如下:
QueryJfrEvent qevent= new QueryJfrEvent(); if(qevent.isEnabled()){ String name = span.name().intern(); // 主动入池 System.out.println(" Check ---- " + name); qevent.eventName = name; qevent.eventName2 = (name + "-bla-bla-bla").intern(); // 拼接后主动入池 qevent.eventName3 = "test-test-test"; qevent.timestamp = span.startTimestamp(); qevent.commit(); // 注意原代码此处漏写方法括号,属于笔误 }
- 修改你使用的
test.jfc配置文件,为trace.dbquery事件显式开启字符串池持久化配置,找到配置中对应自定义事件的段落,添加如下配置项:
<event name="trace.dbquery"> <setting name="enabled">true</setting> <setting name="stackTrace">true</setting> <setting name="stringPool">true</setting> <!-- 强制开启该事件的动态字符串池录制 --> </event>
- 升级JDK版本到17及以上,该问题在JDK 17正式版中已经被修复,无需修改任何业务代码即可正常运行。
内容的提问来源于stack exchange,提问作者srg321
相关产品推荐
相关产品推荐

