应换掉Parallel GC当应用有明确延迟要求(如P99<4GB)、对象生命周期差异大或GC pause频繁超500ms;此时G1或ZGC更合适,但需匹配JDK版本与平台。

什么时候该换掉默认的Parallel GC
Java 11+ 默认用 Parallel GC,吞吐量优先,但停顿时间不可控。如果你的应用有明确的延迟要求(比如接口 P99 4GB)、对象生命周期差异大(大量短命 + 少量长命对象),或者频繁出现 GC pause > 500ms 的告警,那就别硬扛了——Parallel GC 不是万能的。
常见错误现象:Full GC 频繁、Metaspace OOM 伴随长时间 STW、监控里看到 G1 Evacuation Pause 比 Parallel GC 的 GC pause 更平稳但总耗时略高,这些都不是配置问题,是回收器不匹配。
- 堆 Parallel GC,简单稳定,没理由换
- 堆 4–16GB + 延迟敏感:直接考虑
G1 GC,它比 CMS 更易用,且 Java 14 起 CMS 已被移除 - 堆 > 16GB + 要求亚秒级停顿(如金融实时风控、游戏服):ZGC 是更现实的选择,但注意它目前只支持 Linux/x64 和 Linux/AArch64
G1 GC 的关键参数不是调得越细越好
G1 GC 的设计目标是“可预测停顿”,不是“绝对最短停顿”。它的核心机制是分 Region + 并发标记 + 混合回收,所以参数调整必须围绕这个逻辑来,而不是照搬 CMS 或 Parallel 的思路。
容易踩的坑:-XX:MaxGCPauseMillis 设得太低(比如 50ms),会导致 G1 过度拆分回收集、频繁触发 Young GC、反而增加 CPU 开销;设得太高(比如 1000ms),又失去 G1 的意义。
立即学习“Java免费学习笔记(深入)”;
- 推荐起点:
-XX:+UseG1GC -XX:MaxGCPauseMillis=200,再根据实际 GC 日志里的G1 Evacuation Pause实际耗时微调 -
-XX:G1HeapRegionSize别乱动,默认值(2MB)覆盖绝大多数场景;手动设小了会增加 Region 数量,拖慢并发标记;设大了可能导致大对象直接进 Humongous 区,引发碎片和提前 Full GC - 不要配
-XX:G1NewSizePercent和-XX:G1MaxNewSizePercent除非你真看懂了 G1 的自适应年轻代策略——它本就会动态调整,强行锁死反而破坏平衡
ZGC 在 JDK 11–21 中的实际兼容性断层
ZGC 从 JDK 11 开始实验性支持,但直到 JDK 15 才转为正式特性,JDK 21 是当前 LTS 版本中 ZGC 最成熟的阶段。很多团队卡在 JDK 11/17 上想用 ZGC,结果发现功能残缺或 bug 明显。
典型问题:java.lang.OutOfMemoryError: Java heap space 在 ZGC 下可能不是内存真不够,而是 ZPage 分配失败(尤其在容器环境 cgroup v1 + 内存限制 tight 时);或者 Unsafe.allocateMemory 调用失败,报 std::bad_alloc——这跟 ZGC 的元数据映射机制有关,不是代码问题。
- JDK 11:仅支持 Linux/x64,无类卸载支持,
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC启动后仍可能因反射或 JNI 触发退回到 Serial GC - JDK 17:支持 AArch64,但
ZUncommitDelay默认值不合理,容易在低负载时过早释放内存,导致后续分配抖动 - JDK 21:支持 Windows/AArch64,
-XX:+ZGenerational(分代 ZGC)已稳定,但需额外启用,不默认开启
CMS 已死,但它的教训还在影响 G1/ZGC 配置
CMS 被移除不只是因为并发失败多,更是因为它把“并发标记”和“并发清除”割裂开,导致浮动垃圾难处理、无法压缩、对 CPU 亲和性差。现在很多人调 G1 或 ZGC 时,还在下意识模仿 CMS 的思路:比如拼命压低初始标记时间、回避混合回收、过度依赖 -XX:CMSInitiatingOccupancyFraction 类似的阈值思维。
真实情况是:G1 的混合回收(Mixed GC)本质就是“带压缩的 CMS 清除”,ZGC 的转移(Relocation)全程并发且自动触发——你不需要、也不应该去“预测”什么时候该回收,而应关注 GC 日志里是否持续出现 to-space exhausted(ZGC)或 to-space overflow(G1),那才是真正的瓶颈信号。
- 别再找“等效于 CMSInitiatingOccupancyFraction 的 G1 参数”——
G1用-XX:InitiatingHeapOccupancyPercent,但它只触发并发标记开始,不控制回收时机 - ZGC 完全没有“触发阈值”概念,它的回收由
alloc rate和gc cycle interval共同驱动,日志里Pause Mark Start出现频率高,说明应用分配太快,不是 GC 配置错了,是对象生命周期或缓存策略该优化了 - 所有 GC 器都救不了内存泄漏,如果
jstat -gc显示MU(Metaspace used)持续上涨,先查Classloader泄漏,别急着换 GC
GC 选型不是调参比赛,是结合部署环境、JDK 版本、监控能力、故障响应习惯做的系统判断。ZGC 再快,也掩盖不了没开 GC 日志、看不懂 Unified JVM Logging 格式的问题。










