Skip to content

JVM 线上问题排查工具箱:jmap、jstack、jstat 与 Arthas

凌晨两点告警响起:一个服务在抛 OutOfMemoryError,一个接口 30 秒才响应,还有一个 CPU 打满 100%。JDK 自带的工具足够应付这三种场面——本篇按场景而不是按字母序讲它们。

工具地图

工具用途使用时机
jps列出 Java 进程 PID永远的第一条命令
jmap内存直方图、堆转储OOM、内存泄漏
jstack线程转储、死锁检测请求卡死、服务假死
jstatGC 统计采样观察回收行为
jcmd一站式诊断入口(JDK 8+)上面所有事
JMX + VisualVM远程可视化监控长时间观察
Arthas交互式在线诊断不能重启,但急需答案

场景一:服务 OOM / 内存缓慢增长

目标:找出是哪些对象吃掉了堆。

最可靠的证据是"死亡瞬间"的堆转储。提前埋好开关:

bash
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/dump/

OOM 发生时 JVM 自动写出 .hprof 文件。用 Eclipse MAT 或 VisualVM 打开,看支配树(Dominator Tree):通常一两个对象就占了大半堆。真实事故里的经典元凶:

  • HashMap 当缓存用、没有淘汰策略的无界缓存
  • 线程池里 ThreadLocal 的 List 从不清理;
  • 分页查询写成全量加载——"一个小报表"查了 800 万行进内存。

不想下载大转储时,直方图可以快速看每类的实例数和字节数:

bash
jmap -histo <pid> | head -30        # 所有对象
jmap -histo:live <pid> | head -30   # 仅存活对象——会触发一次 Full GC,避开业务高峰

排在前列的业务类出现成千上万个实例,通常直接指向问题代码。

场景二:接口卡死,服务假死

目标:弄清线程在等什么。

bash
jstack <pid> > thread-dump.txt

在转储里搜 BLOCKED 状态和 Found one Java-level deadlock——JVM 会自动检测经典的锁环并打印涉及的两把锁。典型根因是加锁顺序不一致:方法 A 先锁资源 1 再锁资源 2,方法 B 反过来。修法是全局统一加锁顺序——更好的做法是用 java.util.concurrent 的现成原语替代手写同步。

间隔几秒连抓三次转储:三次都 BLOCKED 在同一把锁上的线程就是卡死的请求;栈在变化的线程只是慢,不是死。

场景三:CPU 飙到 100%

目标:找到烧掉那个核的线程。

bash
top -Hp <pid>              # 列出 JVM 内线程,记下最耗 CPU 的线程号
printf '%x\n' <tid>        # 转十六进制,例如 0x2d64
# 在 jstack 转储里搜 nid=0x2d64

两种常见结局:

  • 线程栈显示你的代码在循环——真正的热点循环 Bug;
  • 排最前面的是 GC 线程——"CPU 问题"其实是 GC 问题。用 jstat -gcutil <pid> 1000 确认:如果 FGC 持续上涨,接着做 GC 日志分析

场景四:用 jstat 持续观察 GC

jstat 对 GC 计数器做无侵入采样:

bash
jstat -gcutil <pid> 1000 10   # 每秒采样一次,共 10 次,输出各代占用百分比
jstat -gc <pid> 1000 10       # 绝对值(KB)

-gcutil 以百分比显示 Eden / Survivor / 老年代 / 元空间占用,外加 YGCFGC 及累计耗时。从一小段采样序列就能估算:

  • 年轻代填充速率——两次采样间 Eden 涨了多少 → 推 Young GC 频率;
  • 平均 Young GC 耗时——YGCT / YGC
  • 晋升压力——每次 Young GC 后老年代涨了多少;
  • Full GC 代价——FGCT / FGC

这套推算流程,正是 EasyGC 报告从完整 GC 日志里自动完成的事情——还附带图表和泄漏检测。

场景五:远程可视化监控

需要长时间观察时,用 VisualVM 等 JMX 客户端远程连接。目标 JVM 需要:

bash
-Dcom.sun.management.jmxremote.port=8888
-Djava.rmi.server.hostname=<服务器IP>
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

WARNING

authenticate=false 会把 JVM 暴露给所有能访问该端口的人。务必绑定内网网卡 + 防火墙限制,永远不要把 JMX 暴露到公网。

VisualVM 的 "Visual GC" 插件能实时渲染各代内存变化——压测时直观"感受"应用的分配模式非常好用。

场景六:不能重启,但急需答案

Arthas(阿里巴巴开源)可以附着到运行中的 JVM 提供交互式命令:dashboard 看全局概况、thread -b 直接定位死锁、jad 反编译确认线上到底跑的是哪个版本、profiler 生成热点火焰图。当生产问题等不来一次重新发布时,它是从症状到根因最快的路。

实用排查顺序

  1. jps → 找到 PID。
  2. jstat -gcutil → 是 GC 问题吗?(是 → GC 日志分析
  3. jstack ×3 → 是锁/延迟问题吗?
  4. jmap -histo 或堆转储 → 是内存问题吗?
  5. Arthas / JMX → 追问现场细节。

相关文章