★ CMS、G1、ZGC 收集器有何区别?如何选型?
结论先行:CMS 是追求低停顿的“标记-清除”老年代收集器,JDK 9 起废弃、14 移除;G1 把整个堆划分为 Region,能提供可预测停顿,是 JDK 9+ 的默认收集器;ZGC 用染色指针与读屏障实现近无停顿并发回收,适合超大堆、毫秒级停顿场景。
三者核心差异
| 对比项 | CMS | G1 | ZGC |
|---|---|---|---|
| 内存模型 | 分代连续空间 | Region 化分代 | Region 化不分代 |
| 核心算法 | 标记-清除 | Region 间复制为主 | 染色指针 + 读屏障 |
| 停顿目标 | 尽量低但不保证 | 可配置期望停顿(默认 200ms) | 停顿与堆大小无关,毫秒级 |
| 碎片问题 | 明显,可触发 Concurrent Mode Failure | Region 复制缓解碎片 | 几乎无碎片 |
| 成熟度 | 已退役 | JDK 9+ 默认,最通用 | 适合超大堆、超低延迟 |
选型建议
- JDK 8 存量服务:CMS 或 G1 都可用;希望停顿更平滑就切 G1(-XX:+UseG1GC)。
- 普通 4~16GB 堆、可接受几十毫秒停顿:G1 开箱即用,只需按需调 MaxGCPauseMillis。
- 上百 GB 堆、要求 1~10ms 稳定停顿:选 ZGC(-XX:+UseZGC)。
- 选收集器本质是在吞吐量、停顿时间、内存占用三者之间取舍,没有绝对最优。
- 评估指标:对比只看两点——GC 时间占总运行时间的比例与最坏停顿(P99),其余指标影响有限。
- 迁移节奏:从 CMS 迁 G1 前先保留 GC 日志做前后对比,小流量灰度验证后再全量切换。
记忆点:并发收集器都在“并发标记/清理”阶段让用户线程继续跑,区别在于内存布局与是否搬移对象。
// 启动参数示例(注释即用法):
// java -Xms8g -Xmx8g -XX:+UseG1GC -XX:MaxGCPauseMillis=100 MyApp
// java -Xms100g -Xmx100g -XX:+UseZGC MyApp
public class CollectorDemo {
public static void main(String[] args) {
// 收集器由启动参数决定,业务代码通常无感知
System.out.println("use: " + Runtime.version());
}
}
常见追问 / 记忆点
- 记忆点:CMS 已退场(碎片+浮动垃圾),G1 是默认(Region+可预测停顿),ZGC 面向超大堆(染色指针+读屏障)。
- 追问:G1 靠 Remembered Set 跟踪跨 Region 引用,这是它能做到停顿可控的关键之一。
- 追问:CMS 并发阶段与用户线程并行会产生“浮动垃圾”,预留空间不足会退化为 Serial Old,停顿反而更大。
- 追问:G1 的 Mixed GC 优先回收垃圾最多的 Region,目标是“用最少的停顿清最多的垃圾”。