Java 性能调优与 JFR 实战
Java 应用跑得慢,到底是哪里慢?JDK Flight Recorder(JFR)是 Oracle 贡献的低开销性能分析工具,生产环境可以安全开启。本文从 JFR 录制、事件分析到火焰图解读,带你用数据定位性能瓶颈,而不是靠猜。
一、为什么选 JFR
性能分析工具有很多:Arthas、VisualVM、JProfiler。但它们要么需要外挂 Agent,要么开销大到不能在生产用。JFR 是 JDK 内置的,开销通常低于 1%,可以在生产环境长时间开启。它录制的是 JVM 内部的细粒度事件:方法采样、对象分配、GC、线程阻塞、锁竞争。
| 工具 | 生产可用 | 开销 | 适用场景 |
|---|---|---|---|
| JFR | 是 | <1% | 生产环境长期监控、事后分析 |
| Arthas | 是 | 中等 | 在线诊断、实时排查 |
| JProfiler | 谨慎 | 较高 | 开发环境深度分析 |
二、启动 JFR 录制
JFR 可以在 Java 启动时开启,也可以在运行时动态录制。
# 方式一:启动时录制 60 秒
java -XX:StartFlightRecording=duration=60s,filename=myapp.jfr -jar app.jar
# 方式二:运行时动态录制(不需要重启)
jcmd <pid> JFR.start name=myProfile duration=120s filename=myapp.jfr
# 方式三:持续录制(适合生产长期监控)
java -XX:StartFlightRecording=disk=true,maxsize=500M,maxage=24h,filename=/var/log/jfr/app.jfr -jar app.jar
第三种方式把 JFR 当作持续监控来用,事件写入磁盘环形缓冲区,出问题时可以导出最近几小时的数据。
duration 指定录制时长;filename 指定输出文件;maxsize 限制磁盘占用;maxage 限制保留时间。生产环境推荐 disk=true 持续录制。
三、用 JMC 分析录制结果
录制得到的 .jfr 文件用 JDK Mission Control(JMC)打开分析。JMC 是 JDK 自带的 GUI 工具。
# 打开 JMC
jmc
# 然后 File -> Open File 选择你的 .jfr 文件
JMC 的几个核心视图:
Memory 页签:看堆内存变化曲线、GC 频率和停顿时间。如果 Full GC 太频繁,说明堆太小或内存泄漏。
Thread 页签:看线程活动时间线,哪些线程在忙、哪些在等锁。
Method Profiling 页签:方法采样结果,按耗时排序。最顶部的方法就是热点方法。
四、火焰图:一眼看出热点
JFR 的方法采样数据可以生成火焰图。横轴是时间占比,柱子越宽说明该方法占用 CPU 越多。从下往上看调用栈:最底层是线程入口,最顶层是叶子方法。
# 用 async-profiler 生成火焰图(也支持 JFR 格式)
# 安装:https://github.com/async-profiler/async-profiler
./profiler.sh -d 60 -f flame.html <pid>
# 或者用 JMC 的 Method Profiling 视图直接看火焰图
看火焰图的技巧:找最宽的那根柱子,它就是 CPU 消耗最多的地方。如果宽柱子在框架代码里(如 Jackson 序列化),说明你的业务逻辑产生了大量序列化开销。
五、常见性能问题定位
问题一:CPU 飙高。看 Method Profiling,找占 CPU 最多的方法。常见原因:正则表达式效率低、循环里做了太多计算、日志格式化太重。
问题二:响应慢但 CPU 不高。看 Thread 页签,大概率是线程在等数据库连接池、等锁、或等下游服务。看 Lock 事件和 Socket Read 事件。
问题三:内存泄漏。看 Memory 页签的堆直方图对比,找持续增长的对象类型。用 jmap -histo 辅助确认。
六、JFR 事件类型
# 列出所有 JFR 事件类型
jcmd <pid> JFR.check
# 常用事件:
# jdk.ExecutionSample 方法采样(CPU 分析)
# jdk.GarbageCollection GC 事件
# jdk.ObjectAllocationInNewTLAB 对象分配
# jdk.JavaMonitorEnter 锁竞争
# jdk.ThreadPark 线程阻塞
# jdk.SocketRead Socket 读取
七、总结
性能调优的第一步不是猜,而是测量。JFR 让你在生产环境以不到 1% 的开销录制完整的性能事件,事后用 JMC 或火焰图慢慢分析。记住三步:先录制,再看火焰图找最宽的柱子,最后针对性优化。不要凭感觉调 JVM 参数。