jstat -gccause比jstat -gc更关键,因为它不仅显示“发生了GC”,还明确揭示“为何触发GC”,如Metadata GC Threshold表明Metaspace不足,需调参或排查类加载器泄漏,而非盲目优化代码。

为什么 jstat -gccause 比 jstat -gc 更关键
因为 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 秒采一次根本抓不到。
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 推荐命令:
jstat -gccause(每 200ms 采一次,共 50 次,覆盖 10 秒窗口)200 50 - 重点盯
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,如果输出-XX:MaxMetaspaceSize=-1(即不限制),反而更危险——它会不断扩容直到耗尽本地内存 - 对比
MC(Metaspace 容量)和MU(已使用):如果MU / MC > 0.95,基本就是瓶颈 - 临时缓解可加:
-XX:MaxMetaspaceSize=256m -XX:MetaspaceSize=128m(设初始值避免频繁扩容) - 根因排查得结合
jmap -clstats看类加载器数量,或用jcmd查 native 内存是否被 class metadata 占满VM.native_memory summary
真正容易被忽略的是:Metaspace GC 不会释放所有元数据,只有**无引用的类加载器及其加载的类**才会被卸载。所以即使调大 MaxMetaspaceSize,如果存在 ClassLoader 泄漏(如 ThreadLocal 持有、OSGi、热部署框架残留),问题只会延后爆发。










