如何将JVM C2编译器统计数据暴露至JMX?
JMX暴露C2编译、去优化及jstat全量编译指标实现方案
原生CompilationMXBean确实只提供总编译时长一个核心指标,完全覆盖不了日常监控需求,下面的方案全部基于JDK内置API实现,不需要依赖第三方组件、不需要修改JDK源码,生产环境可用。
核心实现逻辑
jstat展示的所有编译统计、C2编译计数、去优化相关数据,本身就存在JVM进程内存的PerfData计数器里,只是没有默认注册成JMX可访问的MBean,我们只需要把这些数据读出来,注册到平台MBeanServer即可。
1. 指标数据源说明
1.1 计数类指标(和jstat数据源完全一致)
直接通过JDK自带的jvmstat包读取Perf计数器,和jstat读取逻辑100%对齐,没有数据偏差,常用指标对应的计数器key如下:
- C2编译器编译方法总数:读取
sun.ci.compilerThread.2.compiles(分层编译开启时,tier4层级编译数即为C2编译数,可通过sun.ci.tiered.compiles.tier4获取) - 去优化总次数:读取
sun.ci.deoptimization.total,还可按需细分重编译触发的去优化(sun.ci.deoptimization.recompiled)、异常陷阱触发的去优化(sun.ci.deoptimization.trapped) - jstat
-compiler输出的全量字段对应关系:- Compiled(总编译数):
java.ci.totalCompiles - Failed(编译失败数):
sun.ci.compilation.failures - Invalid(无效编译数):
sun.ci.compilation.invalid - Time(总编译时长):
java.ci.totalCompilationTime(即原生MBean暴露的指标) - FailedType(最近失败类型):
sun.ci.lastFailedType - FailedMethod(最近失败方法):
sun.ci.lastFailedMethod
- Compiled(总编译数):
读取Perf计数器的核心代码示例(JDK8+都支持):
import sun.jvmstat.monitor.MonitoredHost; import sun.jvmstat.monitor.MonitoredVm; import sun.jvmstat.monitor.VmIdentifier; public long getPerfLongValue(String counterName) throws Exception { String pid = String.valueOf(ProcessHandle.current().pid()); MonitoredHost host = MonitoredHost.getMonitoredHost(new VmIdentifier(pid)); MonitoredVm vm = host.getMonitoredVm(new VmIdentifier(pid)); try { return (long) vm.findByName(counterName).getValue(); } finally { vm.detach(); } }
1.2 编译/去优化方法名采集
不需要手写JVMTI Agent,直接用内置JFR事件采集即可,生产环境开销低于1%:
- 启动参数加
-XX:StartFlightRecording:settings=profile,jdk.Compilation#enabled=true,jdk.Deoptimization#enabled=true - 代码里定期轮询JFR记录的
jdk.Compilation、jdk.Deoptimization事件,提取事件里的方法签名、编译层级、去优化原因字段 - 建议只保留最近1000条记录即可,不要全量存储避免内存占用过高
2. 注册自定义MBean
把上面采集到的所有指标封装成标准MBean,注册到平台MBeanServer即可通过JMX访问:
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); ObjectName name = new ObjectName("com.custom:type=JvmCompileMetrics"); CompileMetricsMBean mbean = new CompileMetrics(); // 自行实现的指标持有类 mbs.registerMBean(mbean, name);
注册完成后,就可以通过JMX客户端拉取C2编译数、去优化数、对应方法名、jstat全量编译统计所有指标了。
注意事项
- 分层编译开启时不要直接把总编译数算成C2编译数,一定要按编译层级过滤,tier4才是C2生成的机器码
- JFR采集方法名时记得过滤掉JDK内部方法,避免无效数据占空间
- 如果是JDK8u之前的版本,需要把
tools.jar加到classpath才能使用jvmstat包
内容的提问来源于stack exchange,提问作者greg
相关产品推荐
相关产品推荐

