§2.1.5 JVM调优工具实战
考察意图:能否独立的在生产环境排查问题,使用合适的工具定位根因。
回答样板:
Arthas——线上诊断神器:
阿里开源的Java诊断工具,零侵入,直接attach到运行中的Java进程,线上排查首选。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 启动
java -jar arthas-boot.jar
# 选择目标Java进程即可
# 最常用命令
dashboard # 实时看板:线程/内存/GC/CPU
thread -n 5 # CPU最高的5个线程
thread -b # 找出死锁
jad com.xx.MyService # 反编译已加载的类,验证线上版本
watch com.xx.MyService myMethod "{params,returnObj,throwExp}" -x 3 # 监控方法入参/出参/异常
trace com.xx.MyService myMethod -n 5 # 追踪方法调用链路,找出哪个子步骤耗时最长
tt -t com.xx.MyService myMethod # 记录方法调用(TimeTunnel),支持重放
vmoption # 查看JVM参数
vmtool --action getInstances -c com.xx.MyClass --limit 10 # 获取堆中某类实例
monitor -c 5 com.xx.MyService myMethod # 监控方法调用次数/成功率/平均耗时
|
实际场景:一次线上接口变慢,用Arthas的trace一追踪,发现是下游规则引擎的Drools session创建占了80%耗时——KieSession没做池化每次new一个。加了KieSession对象池后恢复。
JDK自带工具——四件套:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # jps —— 列出Java进程
jps -lv # 列出所有Java进程,显示JVM参数和main类
# jstat —— JVM统计监控(实时看GC)
jstat -gcutil PID 1000 10 # 每秒打印一次GC统计,共10次
# 输出关键列:S0/S1(Eden+Survivor使用率), E(eden), O(old), M(metaspace), YGC/YGCT(youngGC次数/耗时), FGC/FGCT(fullGC)
# 解读:FGC频繁 + FGCT高 → 老年代不足或内存泄漏
# E接近100%然后骤降 → 正常Minor GC
# O持续增长不降 → 内存泄漏信号
# jmap —— 堆内存分析
jmap -heap PID # 堆内存概览(各区域大小和使用率)
jmap -histo:live PID | head -20 # 堆中存活对象统计(top 20,强制执行一次Full GC)
jmap -dump:format=b,file=heap.hprof PID # 堆dump(生产慎用,会STW)
# jstack —— 线程堆栈分析
jstack PID > thread.log # 打印线程堆栈
jstack -l PID # 额外打印锁信息
# grep查找:BLOCKED(锁等待)/ WAITING(等待唤醒)/ TIMED_WAITING(超时等待)/ RUNNABLE(正在执行)
# 看线程名:如果全是http-nio-xxx-WAITING → 数据库或下游服务慢
|
MAT(Memory Analyzer Tool)——堆dump分析:
1
2
3
4
5
6
| # 先用 jmap dump 堆快照,然后用MAT打开分析
# MAT直接点"Leak Suspects Report" → 自动分析内存泄漏嫌疑
# 核心操作:
# 1. Histogram:按类统计对象数量和内存占用,找大对象
# 2. Dominator Tree:支配树——最大的内存持有者
# 3. Path to GC Roots:某个对象为什么没被回收(引用链追溯到GC Root)
|
实际场景:OOM排查,MAT打开dump→ Dominator Tree发现HashMap占3.2GB→ Path to GC Roots找到是定时任务的全量缓存→改成分页+本地缓存解决。
jinfo——查看/修改JVM运行时参数:
1
2
| jinfo -flags PID # 查看所有参数(包括默认值)
jinfo -flag +PrintGC PID # 运行时动态开启GC日志(部分参数支持动态修改)
|
生产环境排查最佳路径:
- 遇到CPU飙升 → Arthas
dashboard + thread -n 5 → 定位到具体线程 → jad反编译验证 → 改代码 - 遇到内存泄漏 →
jstat -gcutil观察O区持续增长 → jmap -dump → MAT分析Dominator Tree → Path to GC Roots → 改代码 - 遇到死锁 →
jstack -l → 搜DEADLOCK → 或Arthas thread -b - 遇到接口变慢 → Arthas
trace → 定位最耗时的子调用 → 深入分析
陷阱提示:只会用jps/jstat但说不清每列含义;线上 jmap -dump不提醒STW风险;不知道Arthas的attach机制(VirtualMachine.attach不需要Agent)。