用途:线上出问题(CPU 高、RT 抖、内存涨、线程卡)时按工具组合定位
| 信息 |
工具 |
回答什么 |
| 线程栈快照 |
jstack、kill -3、Arthas thread |
线程在等什么、有没有阻塞/死锁 |
| 堆快照 |
jmap -dump、-XX:+HeapDumpOnOutOfMemoryError、Arthas heapdump |
谁占着内存、引用链是什么 |
| 实时指标 |
jstat -gcutil、JMX、Prometheus JVM exporter |
GC 频率、各代使用、线程数趋势 |
| 性能剖析 |
async-profiler(cpu/alloc/lock/native)、JFR、Arthas profiler |
时间/分配/锁花在哪一行 |
- 宽度 = 占比,不是耗时绝对值;顶层宽就是热点。
- 分配火焰图(
-e alloc) 找分配大户,比事后调 GC 参数有效。
- 锁火焰图(
-e lock) 找等待热点,注意区分“持锁时间长”与“等待时间长”。
- on-CPU 图平坦说明线程不在计算,多半是阻塞:转去看栈与锁。
- 内核/框架帧可折叠(
--alluser 或过滤),避免噪声掩盖业务栈。
低开销(通常 <1%)的持续事件记录,适合生产常开:
jcmd <pid> JFR.start settings=profile name=rec duration=60s filename=/tmp/rec.jfr
- 事件覆盖:方法采样、GC、 safepoint、内存分配、锁竞争、线程阻塞、IO、类加载。
- 用 JDK Mission Control 打开分析,重点看 GC 页、方法热点页与锁竞争页。
| 症状 |
步骤 |
| CPU 100% |
top -H 找线程 → 转 16 进制 → 在 jstack 中定位栈;或 async-profiler CPU 图 |
| RT 抖动 |
GC 日志 + 安全点日志 → 看停顿是否与应用毛刺对齐;再看锁图与下游 RT |
| 内存持续上涨 |
jstat 看老年代基线 → dump → MAT 支配树(Dominator Tree)找大头 |
| 频繁 Full GC |
GC 日志看触发原因(Metadata GC Threshold / Allocation Failure / Humongous)→ 对应处理 |
| 线程堆积 |
jstack 多次采样对比,看 WAITING/BLOCKED 分布与共同等待对象 |
| 堆外内存涨 |
NMT jcmd VM.native_memory summary + Netty 池指标 |
jmap -dump:live 会触发 Full GC 并造成秒级停顿,大堆慎用,优先用 OOM 自动 dump 或低峰执行。
jstack 相对安全,但多次采样间隔要短(1-2 秒)以捕捉变化。
- Arthas 的
watch/trace 有字节码增强开销,用完 reset;线上禁止长时间 trace 高频方法。
- 所有诊断产物(dump、jfr)注意脱敏与访问控制,堆 dump 含明文敏感数据。
| 误区 |
修正 |
| 只看一次线程栈下结论 |
多次采样看趋势 |
用 top 的进程 CPU 判断 Java 热点 |
需要线程级 + 栈符号对齐 |
| 有 dump 就等于会分析 |
必须看支配树与 GC Root 引用链,而不是搜类名 |
| 火焰图有帧就认为慢 |
宽度才是占比,采样噪声要复测 |
| 生产常开高开销探针 |
用 JFR/采样型工具,控制开销预算 |
- OpenJDK 工具文档(jcmd、jstat、jmap、jstack、JFR)
- Arthas 官方文档(诊断命令与实战案例)
- async-profiler 官方 README(事件类型与用法)