JVM 监控与故障排查工具
一、命令行工具(JDK 自带)
1.1 jps(Java Process Status)
查看 Java 进程。
bash
jps -l # 显示完整类名
jps -v # 显示 JVM 参数
jps -m # 显示 main 方法参数
# 输出示例
12345 com.example.MyApp
12346 org.apache.catalina.startup.Bootstrap
1.2 jstat(JVM Statistics Monitoring Tool)
监控 JVM 统计信息。
bash
# 查看 GC 情况
jstat -gc <pid> 1000 10 # 每秒输出一次,共 10 次
# 输出字段
S0C/S1C # Survivor 区容量
S0U/S1U # Survivor 区使用量
EC/EU # Eden 区容量/使用量
OC/OU # 老年代容量/使用量
MC/MU # 元空间容量/使用量
YGC/YGCT # Young GC 次数/时间
FGC/FGCT # Full GC 次数/时间
GCT # 总 GC 时间
# 查看类加载
jstat -class <pid>
# 查看编译
jstat -compiler <pid>
1.3 jinfo(Configuration Info)
查看和修改 JVM 配置。
bash
# 查看所有参数
jinfo <pid>
# 查看特定参数
jinfo -flag MaxHeapSize <pid>
# 动态修改参数(部分参数支持)
jinfo -flag +PrintGCDetails <pid>
1.4 jmap(Memory Map)
生成堆转储快照。
bash
# 查看堆使用情况
jmap -heap <pid>
# 查看对象分布
jmap -histo <pid> | head -20
# 生成 Heap Dump
jmap -dump:format=b,file=heap.hprof <pid>
# 生成时压缩
jmap -dump:live,format=b,file=heap.hprof <pid>
1.5 jstack(Stack Trace)
生成线程转储快照。
bash
# 查看所有线程
jstack <pid>
# 查找死锁
jstack <pid> | grep -A 20 "deadlock"
# 导出到文件
jstack <pid> > thread_dump.txt
# 查看 native 栈
jstack -m <pid>
线程状态:
RUNNABLE:运行中或可运行BLOCKED:等待获取锁WAITING:无限期等待(wait()/park())TIMED_WAITING:限时等待(sleep()/wait(timeout))
1.6 jcmd(Diagnostic Command)
综合诊断工具(JDK 7+)。
bash
# 列出所有诊断命令
jcmd <pid> help
# 查看堆信息
jcmd <pid> GC.heap_info
# 触发 GC
jcmd <pid> GC.run
# 生成 Heap Dump
jcmd <pid> GC.heap_dump /path/to/heap.hprof
# 查看线程
jcmd <pid> Thread.print
# 查看类加载
jcmd <pid> VM.class_hierarchy
二、可视化工具
2.1 JConsole
JDK 自带的图形化监控工具。
bash
jconsole
功能:
- 内存监控(堆、非堆)
- 线程监控(死锁检测)
- 类加载监控
- MBean 管理
2.2 VisualVM
更强大的图形化工具(JDK 8 内置,JDK 11+ 需单独下载)。
功能:
- 实时监控(CPU、内存、线程、类)
- 性能分析(CPU Profiler、Memory Profiler)
- Heap Dump 分析
- Thread Dump 分析
- MBean 浏览器
2.3 MAT(Memory Analyzer Tool)
Eclipse 提供的内存分析工具。
下载地址 :www.eclipse.org/mat/
功能:
- 分析 Heap Dump
- 查找内存泄漏
- Dominator Tree(支配树)
- Histogram(直方图)
- OQL(对象查询语言)
使用步骤:
- 打开
.hprof文件 - 查看 Leak Suspects Report(自动分析泄漏嫌疑)
- 使用 Dominator Tree 查找大对象
- 分析引用链,找到泄漏源头
2.4 Arthas(阿里开源)
线上诊断神器。
下载地址 :arthas.aliyun.com/
启动:
bash
java -jar arthas-boot.jar
常用命令:
bash
# 查看 dashboard
dashboard
# 监控方法调用
monitor com.example.MyClass myMethod
# 追踪方法调用链路
trace com.example.MyClass myMethod
# 查看方法参数和返回值
watch com.example.MyClass myMethod '{params, returnObj}'
# 热更新代码
redefine /path/to/MyClass.class
# 查看线程
thread
thread -b # 查找阻塞其他线程的线程
# 查看 JVM 信息
vmoption
sysprop
sysenv
三、常见故障排查
3.1 CPU 飙高
排查步骤:
bash
# 1. 找到 CPU 高的 Java 进程
top
# 2. 查看进程中 CPU 高的线程
top -H -p <pid>
# 3. 将线程 ID 转换为 16 进制
printf "%x\n" <tid>
# 4. 查看线程栈
jstack <pid> | grep <16进制tid> -A 20
# 5. 分析代码,找出死循环或频繁计算
3.2 内存泄漏
排查步骤:
bash
# 1. 监控堆内存趋势
jstat -gc <pid> 1000
# 2. OOM 时生成 Heap Dump
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/heap.hprof
# 3. 手动生成 Dump
jmap -dump:live,format=b,file=heap.hprof <pid>
# 4. 用 MAT 分析
# - 查看 Dominator Tree
# - 查找占用内存最多的对象
# - 分析引用链
3.3 死锁
排查步骤:
bash
# 1. 生成线程转储
jstack <pid> > thread_dump.txt
# 2. 查找死锁信息
grep -A 20 "deadlock" thread_dump.txt
# 3. 或者用 JConsole / VisualVM 的死锁检测功能
# 4. 分析持锁关系,修复代码
死锁示例输出:
vbnet
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f... (object 0x000000..., a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f... (object 0x000000..., a java.lang.Object),
which is held by "Thread-1"
3.4 Full GC 频繁
排查步骤:
bash
# 1. 查看 GC 日志
jstat -gc <pid> 1000
# 2. 分析 GC 原因
# - 老年代满?→ 增大堆或优化对象生命周期
# - 元空间满?→ 增大 Metaspace
# - System.gc()?→ 检查代码或添加 -XX:+DisableExplicitGC
# 3. 生成 Heap Dump 分析
jmap -dump:live,format=b,file=heap.hprof <pid>
# 4. 优化建议
# - 减少大对象创建
# - 对象池复用
# - 调整新生代大小
3.5 线程数过多
排查步骤:
bash
# 1. 查看线程数
jstack <pid> | grep "java.lang.Thread.State" | wc -l
# 2. 查看线程详情
jstack <pid> > thread_dump.txt
# 3. 分析线程类型
# - 业务线程过多?→ 检查线程池配置
# - WAITING 线程过多?→ 检查是否有资源等待
# - BLOCKED 线程过多?→ 检查锁竞争
# 4. 优化建议
# - 使用线程池
# - 减少锁粒度
# - 异步化处理
四、Arthas 实战案例
4.1 定位慢方法
bash
# 追踪方法执行时间
trace com.example.OrderService createOrder
# 输出示例
`---ts=2024-01-01 12:00:00;thread_name=http-nio-8080-exec-1;id=1a;is_daemon=true;priority=5;
`---[0.523ms] com.example.OrderService:createOrder()
+---[0.012ms] validateOrder()
+---[0.456ms] saveToDatabase() ← 慢
`---[0.055ms] sendNotification()
4.2 查看方法入参和返回值
bash
# 监控方法调用
watch com.example.UserService getUser '{params, returnObj}' -x 2
# 输出示例
method=com.example.UserService.getUser location=AtExit
ts=2024-01-01 12:00:00; [cost=0.234ms]
param[0]=12345
return=@User[id=12345,name=Alice,email=alice@example.com]
4.3 热更新代码(紧急修复 Bug)
bash
# 1. 编译新代码
javac -cp app.jar MyClass.java
# 2. 上传到服务器
# 3. 热更新
redefine /path/to/MyClass.class
# 4. 验证
注意:只能修改方法体,不能修改方法签名、字段、继承关系。
4.4 查看系统属性
bash
# 查看 JVM 参数
vmoption
# 查看系统属性
sysprop
# 查看环境变量
sysenv
五、面试题精选
Q1:如何排查 CPU 飙高问题?
top找到 CPU 高的进程top -H -p <pid>找到 CPU 高的线程printf "%x"转换线程 ID 为 16 进制jstack查看线程栈- 分析代码,找出死循环或频繁计算
Q2:如何排查内存泄漏?
- 监控堆内存趋势(持续增长)
- OOM 时生成 Heap Dump
- 用 MAT 分析,查找大对象和引用链
- 定位泄漏代码,修复
Q3:jstack 和 jmap 的区别?
jstack:生成线程转储,查看线程状态、死锁jmap:生成堆转储,查看对象分布、内存泄漏
Q4:什么是 Heap Dump 和 Thread Dump?
- Heap Dump:堆内存快照,包含所有对象信息,用于分析内存泄漏
- Thread Dump:线程快照,包含所有线程状态和栈信息,用于分析死锁、CPU 高
Q5:Arthas 的优势是什么?
- 无需重启应用
- 支持线上诊断
- 功能强大(监控、追踪、热更新)
- 非侵入式
Q6:Full GC 频繁怎么排查?
- 查看 GC 日志,确认 GC 原因
- 生成 Heap Dump 分析
- 检查是否有内存泄漏
- 调整堆大小或 GC 参数
- 优化代码,减少对象创建
Q7:如何预防死锁?
- 固定锁顺序
- 使用
tryLock超时 - 减少锁粒度
- 使用
jstack定期检测
Q8:生产环境如何监控 JVM?
- 命令行工具 :
jstat、jmap、jstack - 可视化工具:VisualVM、MAT
- APM 工具:Arthas、SkyWalking、Pinpoint
- 日志监控:GC 日志、应用日志
- 指标监控:Prometheus + Grafana