⭐ 推荐:社区规则条款 V1.0

如何用 JFR 飞行记录器定位 Java 性能瓶颈?

GJ
GJ 资深会员 👑黑卡会员
发布于 2026-10-24 15:32 ·6 浏览 ·0 回复
内容摘要

用 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 暂停与分配采样。
  • 采样数据是统计结果,只用于定位方向,结论要靠改完再录一次对比来确认。
本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2501.html
转载请注明出处,版权归原作者所有。
他们都看过 1 人浏览过
GJ论坛站长
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 0

还没有回复,来抢沙发~