JDK 自带工具:jstat /jstack/jmap
前提:
这些命令属于 JDK bin,不是 JRE;
只能抓本地 Java 进程,需要知道进程 PID,查看 PID 命令:jps -l(最常用)
jps = Java Virtual Machine Process Status Tool,JDK 自带工具,专门查看 Java 进程,不需要额外安装。
-l 是 long(长格式) 作用:输出 PID + 主类全限定名 /jar 完整路径
# 查看java进程,拿到pid
jps -l
假设示例中 Java 进程 PID = 12345
一、jstat:JVM 运行状态统计工具(看 GC、类加载、编译等指标)
全称:JVM Statistics Monitoring Tool,JDK 自带,
实时采样监控 JVM 运行统计,最常用就是看 GC 情况,
jstat [-options] <pid> [间隔ms] [采样次数]
常用 option
| gc | GC 统计(最常用) |
| gccapacity | 堆各区域容量 + 当前占用 |
| gcutil | GC 百分比视图(推荐,一眼看使用率) |
| gccause | gcutil + 最后一次 GC 原因 |
| class | 类加载统计 |
| compiler | JIT 编译统计 |
场景 1:排查频繁 FullGC,观察 GC 指标(最常用)
需求:每 1000ms 输出一次,一共输出 10 次,查看 S0、S1、E、O、M 的GC 占比
jstat -gcutil 12345 1000 10
示例输出:
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 89.23 45.12 62.31 94.11 91.23 2145 32.123 12 2.456 34.579
字段解释:
- S0/S1:Survivor0、Survivor1 使用率
- E:Eden 区使用率
- O:老年代 Old 使用率
- M:元空间 Metaspace 使用率
- YGC:年轻代 GC 次数;YGCT:YGC 总耗时
- FGC:FullGC 次数;FGCT:FullGC 总耗时
- GCT:GC 总耗时
✅ 判断点:
场景 2:查看堆内存各区域容量
jstat -gccapacity 12345 1000 5
可以看到 Eden、Survivor、Old 的最大容量、当前容量。
场景 3:查看类加载情况(排查类泄漏、元空间满)
jstat -class 12345
输出:加载类数量、卸载数量、耗时。如果类持续不断加载不卸载 → 元空间溢出。
二、jstack:线程栈分析工具(死锁、线程阻塞、CPU 高、线程池打满)
Java Stack Trace,JDK 自带工具,打印 JVM 内所有线程栈快照
作用:
- Java 死锁
- CPU100%(线程无限循环)
- 线程阻塞、等待锁
- 线程池耗尽、接口长时间 hang 住
语法:
jstack [-l] <pid>
- -l:额外输出锁信息(long listing),排查死锁必加
⚠️ 如果进程卡死,可以加 -F 强制 dump(jstack -F 12345),但 – F 抓取信息会丢失部分信息,优先不用。
✅ 一个进程里面包含多个线程。进程是资源容器,线程是 CPU 调度的最小单位。
场景 1:线上 CPU 飙升,定位哪段代码占用 CPU
完整排查流程:
- -p 12345:只监控 PID=12345 这个进程
- -H:开启线程模式,把进程内部所有线程展示出来(top 默认只展示进程,不展开线程)
场景 2:排查死锁
jstack -l 12345 > deadlock.log
jstack 会自动检测死锁,日志末尾会输出 Found one Java-level deadlock,打印两个线程互相等待的锁和代码位置。
示例死锁片段:
Found one Java-level deadlock:
=============================
"Thread-A":
waiting to lock monitor 0x00007f8b1c003210 (object 0x000000076ac21498, a java.lang.Object),
which is held by "Thread-B"
"Thread-B":
waiting to lock monitor 0x00007f8b1c003320 (object 0x000000076ac214a8, a java.lang.Object),
which is held by "Thread-A"
场景 3:接口响应缓慢,大量线程 WAITING/BLOCKED
jstack 12345 > stack.log
看线程状态:
- BLOCKED:被锁阻塞
- WAITING:等待唤醒(lock.wait () / 线程池队列等待)
- TIMED_WAITING:限时等待(sleep)
- RUNNABLE:运行中(IO 阻塞也会显示 RUNNABLE)
常见坑:大量线程卡在数据库连接池等待,就是大量 WAITING。
三、jmap:内存映像工具,堆内存分析(OOM、内存泄漏)
全称:Java Memory Map,JDK 自带堆内存分析工具。
核心能力:查看堆配置、查看当前堆使用情况、dump 堆快照文件,
用于分析 OOM、内存泄漏。
语法:
jmap [option] <pid>
常用 option
- histo:所有对象;histo:live 只统计存活对象(会触发 FullGC)
⚠️ dump 堆文件会暂停应用,生产尽量业务低峰操作;hprof 文件建议下载本地,用 MAT(Memory Analyzer Tool)分析。
场景 1:快速查看 JVM 堆配置(Xmx/Xms/ 新生代比例)
jmap -heap 12345
输出:堆最大最小,Eden、Survivor、Old 大小,垃圾收集器类型。
场景 2:查看堆中对象数量,初步判断内存泄漏
jmap -histo:live 12345 > histo.log
输出按占用内存排序:
num instances bytes class name
1 1200000 48000000 java.util.ArrayList
2 800000 32000000 com.xxx.entity.User
如果自定义实体类数量持续暴涨,大概率内存泄漏。
场景 3:OOM 发生,导出堆快照(最常用)
jmap -dump:format=b,file=heap_20261008.hprof 12345
拿到 heap_20261008.hprof 文件,下载到本地,用 MAT 工具打开:
- 看大对象
- 看对象引用链,找出是谁持有对象没有释放
推荐:JVM 启动参数增加 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx/,OOM 时自动 dump 堆文件,不用手动执行 jmap。
不推荐场景:生产不要频繁执行 jmap -histo:live
因为加了:live会触发 FullGC,线上业务会卡顿。
四、三个工具对比总结
| jstat | GC 指标、类加载、JIT | 持续监控 GC,判断是否有 GC 异常 |
| jstack | 线程栈、锁、线程状态 | CPU 高、死锁、接口 hang、线程阻塞 |
| jmap | 堆内存、对象、堆 dump | OOM、内存泄漏,分析对象占用 |
五、一套完整线上故障排查流程示例
现象:服务接口变慢,时不时 OOM,老年代持续上涨
六、扩展
Q1:jstat -gcutil 各个字段含义?FGC 持续上涨代表什么?
答:
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
- S0:Survivor0 使用率;S1:Survivor1 使用率
- E:Eden 区使用率;O:老年代使用率
- M:元空间 Metaspace 使用率;CCS:压缩类空间使用率
- YGC:年轻代 GC 次数;YGCT:YGC 总耗时
- FGC:FullGC 次数;FGCT:FullGC 总耗时
- GCT:GC 总耗时
FGC 持续上涨分两种情况:
面试加分:FGC 非常耗时间,会 STW,频繁 FGC 会造成接口超时、服务卡顿。
Q2:jstack 怎么排查 CPU100%?完整步骤
答:
注意:jstack 输出里的 nid 是十六进制,top 看到的线程号是十进制,很多人这里踩坑。
Q3:jstack 看到大量 BLOCKED、WAITING 分别是什么含义?
答:
- BLOCKED:线程正在等待监视器锁(synchronized),拿不到锁被阻塞;大概率锁竞争激烈、死锁;
- WAITING:无限等待,等待被其他线程唤醒,如 Object.wait()、LockSupport.park();常见:线程池无任务、数据库连接池拿不到连接;
- TIMED_WAITING:限时等待,Thread.sleep()、wait(time);
- RUNNABLE:运行中;注意:网络 IO 阻塞时,线程状态依然是 RUNNABLE,这点很容易答错。
Q4:jmap -histo 和 jmap -histo:live 的区别?生产慎用哪个?为什么?
答:
- jmap -histo pid:打印堆中所有对象统计,不触发 FullGC;
- jmap -histo:live pid:只统计存活对象,执行前会触发一次 FullGC。
生产慎用:histo:live,因为 FullGC 会 STW,业务请求卡顿,高峰期可能雪崩。
Q5:jmap dump 堆快照有什么风险?生产什么时候 dump?
答: 风险:
最佳实践:
Q6:jstat、jstack、jmap 三者分工,故障排查时使用顺序?
答:
- jstat:宏观监控,看 GC 指标,先判断有没有内存 / GC 问题;
- jstack:线程层面,查 CPU 高、死锁、接口 hang;
- jmap:内存对象层面,OOM、内存泄漏,dump 堆快照。
排查顺序: jps → jstat(确认 GC 异常)→ jstack(看线程是否阻塞)→ jmap(导出堆,定位泄漏对象)
Q7:jstack -F 参数作用?缺点?
答: jstack pid 正常抓取;当进程卡死,正常 jstack 无法响应时,使用 jstack -F pid 强制 dump。 缺点:强制抓取会丢失部分信息,丢失 Java 锁信息,优先不用。
Q8:容器环境使用这些命令有什么坑?
答: 容器内 Java 进程 PID 是容器内 PID,宿主机看不到; 解决:启动容器添加 –pid=host,共享宿主机 pid 命名空间; 另外注意:JDK 镜像,必须装 JDK,不能只用 JRE(缺少 jstat/jstack/jmap 工具)。
Q9:OOM 一定是内存泄漏吗?
答:不一定。
可以通过 jstat 观察区分:泄漏是缓慢持续涨老年代;大对象是瞬间把 Old 打满。
Q10:jstat 可以看线程堆栈吗?jstack 能看 GC 指标吗?
答: 都不行。 jstat:只输出统计指标,看不到代码堆栈; jstack:只打印线程栈,不统计 GC、内存占用; 各司其职。
