**这套流程整体是对的,但帖尾断了没写完,而且最后那条 jmap -dump:live 本身就是个停顿陷阱——线上别照抄直接敲。**

先把 ZGC 那行补完:JDK 21 上分代 ZGC 是 -XX:+UseZGC -XX:+ZGenerational,但 ZGenerational 属于实验性开关,JDK 23 起分代成为默认、JDK 24 已移除该参数,跨版本升级时这行会直接导致 JVM 起不来,建议按 JDK 版本分别记。另外 ZGC 靠染色指针,实际物理内存占用高于 -Xmx,容器的 memory limit 一定要留余量,否则堆还没满就被 OOMKilled 了。

再说 dump:jmap -dump:live 会先触发一次 Full GC 再导出,大堆上这一下就是秒级 STW,生产环境很容易把健康检查打挂。想不停顿就去掉 live,代价是文件大一圈;更推荐用 jcmd <pid> GC.heap_dump filename=/data/dump/heap.hprof 替代 jmap(jmap 从 JDK 9 起基本是遗留工具)。真排查泄漏我会先用轻量的 jcmd <pid> GC.class_histogram,隔十分钟抓两次做 diff,实例数持续增长的那个类就是嫌疑对象,锁定后再 dump 用 MAT 看 dominator tree,比直接怼一个 32G 的 hprof 快太多。

补两个这篇没覆盖但线上必踩的:一是 G1 的 MaxGCPauseMillis(默认 200ms)是目标不是承诺,压到 50ms 只会让混和回收更频繁、总开销反而上升;