CMS虽在JDK14被移除,但老系统因升级成本高(监控脚本、SLA习惯等)仍在JDK8/11上使用;其无并发整理致碎片化,Concurrent Mode Failure引发秒级STW;G1的-XX:MaxGCPauseMillis是软目标,需配合参数调优。

CMS 停止维护后为什么还有人在用?
CMS 在 JDK 9 中被标记为 @Deprecated,JDK 14 正式移除,但部分老系统仍运行在 JDK 8u292 或 JDK 11(未升级 GC)上,实际仍在用。这不是因为 CMS 多先进,而是升级成本高:依赖 CMS 特性(如 -XX:CMSInitiatingOccupancyFraction 控制触发时机)的监控脚本、GC 日志解析工具、甚至业务方对“停顿时间稳定在 80ms 内”的 SLA 习惯,都让迁移变得谨慎。
关键点在于:CMS 没有并发整理阶段,长期运行必然产生碎片,Concurrent Mode Failure 会触发 Full GC,这时 STW 时间可能飙升到秒级——这比 G1 的混合回收更难预测。所以现在提 CMS,基本只出现在“如何安全下线它”的场景里。
G1 的 -XX:MaxGCPauseMillis 是摆设吗?
不是摆设,但它是软目标(soft goal),JVM 尽力满足,不保证每次达标。-XX:MaxGCPauseMillis=200 意味着 G1 会动态调整年轻代大小、混合回收的 CSet(Collection Set)范围、甚至降低吞吐量来换停顿时间。但若堆内大对象多(比如 >50% _HeapRegionSize_)、或者 Mixed GC 跟不上分配速率,就可能超时。
常见误操作:
- 盲目调小
-XX:MaxGCPauseMillis(如设成 50),导致频繁 GC、吞吐暴跌 - 忽略
-XX:G1HeapRegionSize,默认值 1MB 可能让 2MB 对象直接进 Humongous 区,引发额外开销和碎片 - 没配
-XX:G1MixedGCCountTarget和-XX:G1OldCSetRegionThresholdPercent,导致老年代回收节奏失控
实测中,G1 在 4–16GB 堆、要求平均停顿
ZGC 在 JDK 11+ 上必须开启 -XX:+UseZGC 吗?
必须。ZGC 不是默认 GC,且从 JDK 11 引入起就要求显式启用:-XX:+UseZGC。它依赖 Linux 的 mmap(MAP_SYNC)(需 kernel 4.14+)或 Windows 的内存映射特性,JVM 启动时会校验环境,失败则报错:Failed to initialize ZGC。
ZGC 的核心优势是 STW 时间基本与堆大小无关(实测 8GB~16TB 堆均控制在
- 仅支持 64 位 Linux/x86_64、Windows x64、macOS(仅 JDK 17+)
- 不支持类卸载(
-XX:+ClassUnloading默认 false,开启会破坏 ZGC 并发标记) - 初始标记和最终标记仍有 STW,但极短(sub-millisecond),而 CMS/G1 的初始标记阶段在堆大时可达几十毫秒
典型适用场景:延迟敏感服务(如风控实时决策、高频交易网关)、堆常驻 32GB+ 且无法接受 >10ms STW 的 Java 进程。
选型时最容易被忽略的兼容性细节
很多团队卡在“ZGC 性能好,但上线就崩”,问题往往不出在 GC 本身,而在生态适配:
- JFR(Java Flight Recorder)需配合
-XX:+UnlockCommercialFeatures(JDK 8u261+)或直接用 JDK 11+,旧版 JFR 与 ZGC 元数据不兼容 - JVM TI 工具(如 Arthas、BTrace)部分 hook 点在 ZGC 下行为异常,
vmtool --action getstatic可能返回 stale value - 某些 JNI 库(尤其直接操作
jobject地址的)在 ZGC 的染色指针(colored pointers)机制下失效,报Accessing invalid oop
真正做选型,不能只看 GC 参数调优文档,得拉出线上真实流量压测,重点观察:GC 线程 CPU 占比、ZStat 输出中的 Pause Mark End 和 Pause Relocate End 是否稳定、以及 OOM 是否从 java.lang.OutOfMemoryError: Java heap space 变成 java.lang.OutOfMemoryError: Compressed class space(ZGC 下元空间压力更明显)。

















