如何通过jstat -gccause命令实时查看触发垃圾回收的具体原因?
- 内容介绍
- 文章标签
- 相关推荐
本文共计959个文字,预计阅读时间需要4分钟。
由于使用 `jstat -gc` 只会告诉你 发生了GC,而 `jstat -gccause` 会明确告诉你 为什么现在发生了GC。例如,看到 `Metadata GC Threshold`,你就知道是 Metaspace 快满了,而不是代码写错、内存泄漏,或者是类加载太多或未及时卸载。这个区别直接决定了你是否需要调整参数、检查类加载器,甚至可能需要升级 Spring Boot 的 auto-configuration。
jstat -gccause 的输出列怎么看
它和 jstat -gc 输出结构一致,但多出两列:LGCC(Last GC Cause)和 CGCC(Current GC Cause)。注意:这两列是字符串,不是数字。
-
LGCC:上一次 GC 是被什么触发的,例如Allocation Failure(Eden 满)、Metadata GC Threshold(Metaspace 达限)、System.gc()(有人手动调了) -
CGCC:当前采样时刻,**正在发生的 GC 是由什么触发的**。如果这一列是No GC,说明此刻没在 GC;如果是Metadata GC Threshold,那说明 JVM 正因 Metaspace 不足而准备或正在进行 GC - 常见值还有:
G1 Evacuation Pause(G1 在做年轻代回收)、Allocation Profiling(ZGC 启用了分配采样)、Full GC(老年代真的撑不住了)
怎么用才不漏掉关键触发点
单次执行容易错过瞬时 GC,必须加时间间隔和次数,否则可能只看到 No GC 就以为没事——实际 GC 可能每 200ms 触发一次,你 1 秒采一次根本抓不到。
- 推荐命令:
jstat -gccause <pid> 200 50(每 200ms 采一次,共 50 次,覆盖 10 秒窗口) - 重点盯
CGCC列连续出现的非No GC值,比如反复出现Metadata GC Threshold,说明 Metaspace 长期处于临界状态 - 别只看最后一行——GC 原因可能在第 3 行就首次暴露,后面几行只是重复;要扫完整输出,尤其关注第一次非
No GC出现的位置 - 如果
CGCC是System.gc(),立刻检查代码里有没有System.gc()或启用了-XX:+ExplicitGCInvokesConcurrent
看到 Metadata GC Threshold 后该做什么
这不是警告,是已发生的事实:JVM 已经因为 Metaspace 不足触发了 GC,且大概率失败了(否则不会继续报这个原因),接下来可能频繁 Full GC 或 OOM。
- 先确认 Metaspace 当前配置:
jinfo -flag MaxMetaspaceSize <pid>,如果输出-XX:MaxMetaspaceSize=-1(即不限制),反而更危险——它会不断扩容直到耗尽本地内存 - 对比
MC(Metaspace 容量)和MU(已使用):如果MU / MC > 0.95,基本就是瓶颈 - 临时缓解可加:
-XX:MaxMetaspaceSize=256m -XX:MetaspaceSize=128m(设初始值避免频繁扩容) - 根因排查得结合
jmap -clstats <pid>看类加载器数量,或用jcmd <pid> VM.native_memory summary查 native 内存是否被 class metadata 占满
真正容易被忽略的是:Metaspace GC 不会释放所有元数据,只有**无引用的类加载器及其加载的类**才会被卸载。所以即使调大 MaxMetaspaceSize,如果存在 ClassLoader 泄漏(如 ThreadLocal 持有、OSGi、热部署框架残留),问题只会延后爆发。
本文共计959个文字,预计阅读时间需要4分钟。
由于使用 `jstat -gc` 只会告诉你 发生了GC,而 `jstat -gccause` 会明确告诉你 为什么现在发生了GC。例如,看到 `Metadata GC Threshold`,你就知道是 Metaspace 快满了,而不是代码写错、内存泄漏,或者是类加载太多或未及时卸载。这个区别直接决定了你是否需要调整参数、检查类加载器,甚至可能需要升级 Spring Boot 的 auto-configuration。
jstat -gccause 的输出列怎么看
它和 jstat -gc 输出结构一致,但多出两列:LGCC(Last GC Cause)和 CGCC(Current GC Cause)。注意:这两列是字符串,不是数字。
-
LGCC:上一次 GC 是被什么触发的,例如Allocation Failure(Eden 满)、Metadata GC Threshold(Metaspace 达限)、System.gc()(有人手动调了) -
CGCC:当前采样时刻,**正在发生的 GC 是由什么触发的**。如果这一列是No GC,说明此刻没在 GC;如果是Metadata GC Threshold,那说明 JVM 正因 Metaspace 不足而准备或正在进行 GC - 常见值还有:
G1 Evacuation Pause(G1 在做年轻代回收)、Allocation Profiling(ZGC 启用了分配采样)、Full GC(老年代真的撑不住了)
怎么用才不漏掉关键触发点
单次执行容易错过瞬时 GC,必须加时间间隔和次数,否则可能只看到 No GC 就以为没事——实际 GC 可能每 200ms 触发一次,你 1 秒采一次根本抓不到。
- 推荐命令:
jstat -gccause <pid> 200 50(每 200ms 采一次,共 50 次,覆盖 10 秒窗口) - 重点盯
CGCC列连续出现的非No GC值,比如反复出现Metadata GC Threshold,说明 Metaspace 长期处于临界状态 - 别只看最后一行——GC 原因可能在第 3 行就首次暴露,后面几行只是重复;要扫完整输出,尤其关注第一次非
No GC出现的位置 - 如果
CGCC是System.gc(),立刻检查代码里有没有System.gc()或启用了-XX:+ExplicitGCInvokesConcurrent
看到 Metadata GC Threshold 后该做什么
这不是警告,是已发生的事实:JVM 已经因为 Metaspace 不足触发了 GC,且大概率失败了(否则不会继续报这个原因),接下来可能频繁 Full GC 或 OOM。
- 先确认 Metaspace 当前配置:
jinfo -flag MaxMetaspaceSize <pid>,如果输出-XX:MaxMetaspaceSize=-1(即不限制),反而更危险——它会不断扩容直到耗尽本地内存 - 对比
MC(Metaspace 容量)和MU(已使用):如果MU / MC > 0.95,基本就是瓶颈 - 临时缓解可加:
-XX:MaxMetaspaceSize=256m -XX:MetaspaceSize=128m(设初始值避免频繁扩容) - 根因排查得结合
jmap -clstats <pid>看类加载器数量,或用jcmd <pid> VM.native_memory summary查 native 内存是否被 class metadata 占满
真正容易被忽略的是:Metaspace GC 不会释放所有元数据,只有**无引用的类加载器及其加载的类**才会被卸载。所以即使调大 MaxMetaspaceSize,如果存在 ClassLoader 泄漏(如 ThreadLocal 持有、OSGi、热部署框架残留),问题只会延后爆发。

