环境准备与基础信息
环境要求:Linux 系统,JDK8 或 JDK11+。
第一步:获取 Java 进程 PID
# 方法一:使用 jps
jps -l
方法二:使用 ps
ps -ef | grep java
假设获取到的进程 PID 为 12345,下文所有命令中的 PID 请替换为你的实际进程 ID。
一、线程堆栈 ThreadDump(现场抓取,建议多次抓取对比)
ThreadDump 用于排查死锁、线程阻塞、CPU 飙高等问题。
方式 1:jstack(最常用)
# 输出线程栈到文件
jstack 12345 > thread_dump_12345_$(date +%Y%m%d_%H%M).txt
连续抓取 3 次,间隔 2 秒(便于观察线程状态变化)
jstack 12345 > thread1.txt
sleep 2
jstack 12345 > thread2.txt
sleep 2
jstack 12345 > thread3.txt
强制模式(当进程无响应时使用)
jstack -F 12345 > thread_dump_force.txt
方式 2:kill -3(不杀死进程,仅输出线程栈)
kill -3 12345
⚠️ 注意:这是 kill -3,不是 kill -9。kill -3 只是向进程发送信号,用于打印线程栈到标准输出(通常为 catalina.out 或应用日志)。
二、内存快照 HeapDump(.hprof 文件,排查 OOM、内存泄漏)
1. jmap 手动触发 dump(进程存活时抓取)
# 完整堆 dump,live=true 只 dump 存活对象(推荐,过滤死亡对象,文件更小)
jmap -dump:live,format=b,file=heap_12345_$(date +%Y%m%d_%H%M).hprof 12345
参数说明:
- live:执行 FullGC,只保存存活对象,文件更小;去掉 live 会 dump 全部对象(包括垃圾对象)。
- format=b:二进制 hprof 格式,MAT 等工具可识别。
# 强制模式(当进程无响应时使用)
jmap -dump:live,format=b,file=heap_force.hprof -F 12345
2. OOM 自动 dump(需事前配置,故障发生后无法临时添加)
在 JVM 启动参数中添加以下配置,发生 OOM 时自动生成 hprof 文件:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/logs/heapdump.hprof
两种 OOM 情况:
业务恢复手段:
- 进程僵死(进程存在但接口无响应、页面卡死):先抓现场,再重启进程,切勿直接 kill。
- 进程已被系统 OOM Killer 杀死:直接启动应用,恢复业务。
- 多实例部署:若集群有多台机器,可先下线故障节点,由其他机器承接流量,保证业务可用性,再排查故障机器。
三、GC 日志(重点:GC 日志不能事后抓取!必须 JVM 启动时预先开启)
JDK8 GC 日志启动参数
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-Xloggc:/data/logs/gc-%t.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=100M
JDK11+ 使用统一 Xlog(新 API)
-Xlog:gc*:file=/data/logs/gc-%t.log:time,uptimemillis:filecount=10,filesize=100M
故障现场实时查看 GC 状态(jstat,实时采样)
# 每 1000ms 输出一次,共输出 20 次
jstat -gc 12345 1000 20
重点监控指标:YGC(Young GC 次数)、YGCT(Young GC 时间)、FGC(Full GC 次数)、FGCT(Full GC 时间)、GCT(总 GC 时间)。关注 FullGC 次数和总停顿时间。
查看 GC 日志:
- 若 JVM 启动参数已配置 GC 日志,直接查看日志文件,关注频繁 FullGC、OOM 异常;
- 若未开启历史 GC 日志,可现场实时采样 GC 状态:
jstat -gc PID 1000 30 > jstat_gc.log
查看应用 stdout/catalina.out 日志:
# 搜索 OOM 异常堆栈
grep -n "OutOfMemoryError" app.log
若进程已被杀死、不复存在:无法通过 jstack / jmap 抓取 dump,只能依赖历史监控、GC 日志、应用日志进行复盘。
四、故障现场完整操作脚本(可直接复制执行)
PID=12345
DATE=$(date +%Y%m%d_%H%M%S)
1. 连续 3 次线程 dump
jstack $PID > thread_dump_${DATE}1.txt
sleep 2
jstack $PID > thread_dump${DATE}2.txt
sleep 2
jstack $PID > thread_dump${DATE}_3.txt
2. 堆内存快照(注意:大堆 dump 会卡顿业务!确保磁盘空间充足)
jmap -dump:live,format=b,file=heapdump_${DATE}.hprof $PID
3. 实时 GC 采样
jstat -gc $PID 1000 10 > jstat_gc_${DATE}.log
五、工具总结清单
| 线程堆栈 | jstack PID > xxx.txt | .txt | 死锁、CPU 高、线程阻塞 | 抓 3 次对比;卡死用 -F |
| 堆内存快照 | jmap -dump:live,format=b,file=xxx.hprof PID | .hprof | 内存泄漏、OOM 分析 | 大堆会 STW,占磁盘 IO |
| GC 历史日志 | JVM 启动参数 -Xloggc | gc-xxx.log | GC 停顿、频繁 FullGC | 必须提前开启,事后无法抓取 |
| 实时 GC 状态 | jstat -gc PID 1000 20 | 控制台输出 | 现场看当前 GC 情况 | 快照采样,不是历史日志 |
| 线程栈备选 | kill -3 PID | 输出到应用 stdout/catalina.out | 线程栈 | 不会杀死进程 |
六、常见注意事项
- jmap dump 大堆(>8G)会造成业务卡顿,尽量在业务低峰期执行;故障迫不得已时才执行。
- GC 日志必须 JVM 启动时配置,进程已经挂了就拿不到 GC 历史。
- hprof 文件需要 MAT 工具解析,不要直接使用 vim 打开。
- 执行 jstack/jmap 需要和 Java 进程同用户,root 用户有时会因权限问题导致异常。




