如何用 JFR 飞行记录器定位 Java 性能瓶颈?
用 JFR 定位 Java 性能瓶颈的流程:先确认 JDK 版本及 JFR 可用性,通过启动参数或 jcmd 对运行中进程录制 .jfr 文件,再用 JMC 或命令行分析,并依据 ExecutionSample、JavaMonitorEnt
学完这篇,你能在自己的 Java 服务上用 JFR 录一段运行数据,并从中找到最耗 CPU 的方法、锁等待点或者 GC 压力来源。
第一步:确认 JDK 版本和 JFR 是否可用
这一步要确认你手上的 JDK 能否直接使用 JFR,以及用哪种工具读取录出来的文件。JFR 从 JDK 8u262 起免费开放,不用再加 -XX:+UnlockCommercialFeatures;JDK 11 及以上默认内置。先执行:
java -version
jps -l # 拿到目标进程 PID
jcmd <PID> help # 输出里出现 JFR.start / JFR.dump / JFR.stop / JFR.check 即可用
另外注意:独立的 jfr 命令行工具是 JDK 12 才有的,JDK 11 只能用 JDK Mission Control(JMC 7.x 以上)打开 .jfr 文件。
注意:如果是 JDK 8 且版本低于 8u262,启动参数里必须加
-XX:+UnlockCommercialFeatures -XX:+FlightRecorder,否则 JVM 会直接报错退出。
第二步:录制一段飞行记录
这一步要产出一个 .jfr 文件。有两种方式,生产环境推荐第二种。
方式一,启动时挂上:
java -XX:StartFlightRecording=filename=/tmp/app.jfr,settings=profile,duration=120s -jar app.jar
方式二,对已经在跑的进程临时录制:
jcmd <PID> JFR.start name=diag settings=profile duration=120s filename=/tmp/diag.jfr
jcmd <PID> JFR.check name=diag # 看进度
jcmd <PID> JFR.dump name=diag filename=/tmp/diag.jfr # 提前导出
jcmd <PID> JFR.stop name=diag filename=/tmp/diag.jfr # 手动结束并落盘
settings 有两个内置模板:default 大约 1% 开销,profile 大约 2% 开销、采样更密。排查性能瓶颈用 profile,长期挂着观察用 default。
第三步:打开文件做第一轮筛选
这一步要快速判断问题落在 CPU、锁、内存还是 IO 上。图形界面用 JMC:菜单 File 里的 Open File 打开 .jfr,先看 Automated Analysis 页,它会直接把可疑项列出来;再看 Method Profiling 页。
纯命令行也行,先用摘要看事件量:
jfr summary /tmp/diag.jfr
jfr print --events jdk.ExecutionSample --stack-depth 64 /tmp/diag.jfr > samples.txt
第四步:用具体事件对应具体瓶颈
这一步要把事件名翻译成结论。
- CPU 打满:看
jdk.ExecutionSample,按栈顶方法统计出现次数,占比最高的就是热点方法,往下看调用链找到入口。 - 接口慢但 CPU 不高:看
jdk.JavaMonitorEnter的monitorClass(谁在抢锁)和jdk.ThreadPark(谁在等),栈里通常能直接看到业务类名。 - GC 频繁:看
jdk.GCPhasePause的暂停时长,再配合分配采样jdk.ObjectAllocationSample(JDK 16+)或jdk.ObjectAllocationInNewTLAB(更早版本)确认是谁在疯狂创建对象。 - 卡在外部依赖:看
jdk.SocketRead、jdk.FileRead、jdk.FileWrite的时长分布。
注意:
jdk.ExecutionSample默认每 10~20 毫秒采样一次,是抽样统计而非全量记录。看到某方法占比 30% 只能说明它值得优化,不能当成精确耗时。
注意:单次录制不要拉太长,120 秒的
profile记录轻松几百 MB,先确认目标机器磁盘空间。
第五步:改完再录一次做对比
这一步要验证优化是否真的生效。用完全相同的 settings 和 duration 再录一份,对比两次的 jdk.ExecutionSample 栈分布和 jdk.GCPhasePause 总暂停时间,差值才是可信的收益。顺手把两份 .jfr 和结论整理成帖,发到社区「技术交流 › 后端开发 › Java」子分类,别人复现你的场景会快很多。
小结
- JFR 免费门槛是 JDK 8u262,JDK 11+ 直接可用,
jfr命令要 JDK 12+。 - 生产排查用
jcmd <PID> JFR.start ... settings=profile duration=120s,别用启动参数改配置重启。 - CPU 看
jdk.ExecutionSample,锁看jdk.JavaMonitorEnter和jdk.ThreadPark,内存看 GC 暂停与分配采样。 - 采样数据是统计结果,只用于定位方向,结论要靠改完再录一次对比来确认。
转载请注明出处,版权归原作者所有。