jstat GC 监控实战
更新时间:2026-09-18。jstat 是最轻量、无需导文件的实时 GC 观测手段,适合先判断「是不是 GC 的问题」。分代基础见 JVM 运行时内存与堆分代,深度排查见 Full GC 频繁怎么排查。
一、常用命令
bash
jps # 找 PID
jstat -gcutil <pid> 1000 10 # 每 1s 一次,共 10 次(看百分比)
jstat -gc <pid> 1s # 看各区容量/使用量(KB)与 GC 统计
jstat -gccause <pid> 1s # 额外显示最近一次/当前 GC 原因
jstat -gcnew / -gcold / -gcmetacapacity # 只看新生代/老年代/元空间二、列含义(-gcutil)
| 列 | 含义 |
|---|---|
| S0/S1 | 两个 Survivor 使用率 |
| E | Eden 使用率 |
| O | 老年代使用率 |
| M | Metaspace 使用率 |
| YGC/YGCT | Young GC 次数 / 累计耗时(秒) |
| FGC/FGCT | Full GC 次数 / 累计耗时(秒) |
| GCT | GC 总耗时 |
三、怎么判读
1. 看 GC 频率与单次耗时
- 连续采样看
YGC增长速度和YGCT/YGC平均 Young GC 耗时(通常几十 ms 内可接受)。 FGC持续增长、单次FGCT长(几百 ms~秒级)是危险信号——Full GC 基本全堆 STW。
2. 看回收效果判断泄漏
- Full GC 后
O(老年代)能明显回落 → 多为内存压力/堆太小/大对象,可先调参。 - Full GC 后
O几乎不降、且基线随时间台阶式抬升 → 强烈疑似内存泄漏,转 jmap 导堆 用 MAT 查引用链。
3. 看 Eden 涨速
- Eden 几秒打满说明分配速率极高(可能是短寿对象洪流),结合 Young GC 后晋升量评估新生代大小。
4. -gccause 看原因
Allocation Failure(正常 Young GC)、Metadata GC Threshold(元空间触发,常见类加载过多)、System.gc()(被显式调用,可加 -XX:+DisableExplicitGC)。
四、和其他工具配合
- jstat 给「宏观趋势」,定位具体代码还要 GC 日志(
-Xlog:gc*,JDK9+;-XX:+PrintGCDetailsJDK8)+ jstack(GC 时业务线程在干嘛)+ jmap(谁占内存)。 - JDK9+ 也可用
jcmd <pid> GC.heap_info、jcmd <pid> VM.metaspace看元空间。
五、常见坑
- 只看某一时刻快照:jstat 价值在连续采样趋势,单次数据无法判断频率。
- 只盯 YGC 次数:Young GC 频繁但又快又能回收是健康的;真正要警惕的是 FGC 多、耗时长、回收少。
- 忽略单位:
-gc容量列是 KB,-gcutil是百分比,别看错。 - 把 jstat 当最终证据:它不告诉你「哪个对象/哪行代码」,泄漏一定要上堆 Dump。
- 容器时差/采样开销:高频采样本身有轻微开销,排障用 1s 间隔即可。
总结
jstat 用 -gcutil/-gc/-gccause 每秒采样各代使用率和 YGC/YGCT、FGC/FGCT:健康系统表现为 Young GC 频繁但单次很快且老年代稳定,危险信号是 FGC 持续增长且单次耗时达数百毫秒以上;再看 Full GC 后老年代能否明显回落——能回落多为堆小/大对象压力,降不下去且基线台阶抬升则是内存泄漏、需用 jmap+MAT 定位;用 -gccause 区分 Allocation Failure、Metadata GC Threshold 和 System.gc,并记住 jstat 只给宏观趋势,最终还要结合 GC 日志、jstack 和堆 Dump 才能定位到代码。