JVM中的CMS、G1、ZGC是不同时代针对不同场景设计的垃圾收集器,分别代表了“低延迟探索”“平衡型实践”“新一代低延迟突破”三个阶段。以下从设计目标、核心机制、性能特性、利弊等维度进行深度对比,并附选型建议。
一、核心维度对比表
| 维度 | CMS(Concurrent Mark Sweep) | G1(Garbage-First) | ZGC(Z Garbage Collector) |
|---|---|---|---|
| 出现版本 | Java 5(2004年),Java 14移除 | Java 7(2011年),Java 9默认 | Java 11(2018年),Java 17稳定版 |
| 设计目标 | 低延迟(优先减少STW时间) | 平衡延迟与吞吐量(可控停顿+高效回收) | 超低延迟+超大堆(亚毫秒级停顿,支持16TB堆) |
| 核心机制 | 并发标记-清除(老年代)+ ParNew(年轻代) | Region化管理+混合回收+停顿预测模型 | 着色指针+读屏障+全阶段并发+Region化 |
| STW阶段 | 初始标记、重新标记(共约10-100ms) | 初始标记、最终标记、筛选回收(共约50-200ms) | 初始标记、最终标记(共约0.1-1ms) |
| 内存管理 | 连续内存(老年代),碎片严重 | Region化(1MB-32MB),低碎片 | Region化(1MB-4GB),几乎无碎片 |
| CPU开销 | 高(并发阶段需2-4个GC线程竞争CPU) | 中(Region管理+记忆集维护) | 中低(读屏障轻量,并发阶段效率高) |
| 内存开销 | 低(无额外Region/记忆集大开销) | 中(记忆集占堆5%-10%) | 中(着色指针需64位地址支持,无额外元数据) |
| 最大堆支持 | 有限(建议≤10GB,大堆易触发Full GC) | 中等(建议≤64GB,更大堆延迟上升) | 超大(最大16TB,大堆下性能稳定) |
| 适用场景 | 旧系统维护(JDK8及以下,2-10GB堆) | 中大型堆(4-64GB),平衡延迟与吞吐量 | 超大堆(≥32GB),亚毫秒级延迟需求 |
二、深度利弊分析
1. CMS:低延迟探索的“先驱”与局限
优势:
- 低延迟基础实现:首次将“并发”思想大规模应用于老年代回收,通过“并发标记+并发清除”将STW时间压缩到100ms内,在G1成熟前是低延迟场景(如电商支付)的唯一选择。
- 对小堆友好:在2-4GB堆中,STW控制能力优于早期G1(G1在小堆下Region管理开销大)。
- 配置简单:核心参数少(如触发阈值、压缩策略),调优门槛低。
劣势:
- 内存碎片致命:并发清除不移动对象,老年代长期运行后碎片严重,大对象分配时易触发Full GC(单线程压缩,STW可达秒级)。
- CPU消耗高:并发阶段GC线程与应用线程抢CPU,4核以下机器吞吐量下降明显(5%-20%)。
- 浮动垃圾风险:并发清除阶段产生的新垃圾(浮动垃圾)需预留10%-20%内存,否则易OOM或触发Full GC。
- 已被废弃:Java 14后移除,无官方维护,新系统不应选用。
2. G1:平衡型GC的“标杆”
优势:
- 延迟与吞吐量平衡:通过“Region化+混合回收”,既能将STW控制在200ms内(默认目标),又保持较高吞吐量(优于CMS,接近ParallelGC),适合多数企业级应用(Web服务、CRM等)。
- 低碎片:筛选回收阶段通过复制存活对象到新Region,自动整理内存,碎片率远低于CMS。
- 动态适应性强:无需手动划分年轻代/老年代,自动调整Region角色,适配不同应用的对象分配特性。
- 成熟稳定:Java 9后成为默认GC,经过多年优化,在4-64GB堆中表现可靠。
劣势:
- 内存开销较高:每个Region的记忆集(Remembered Set)需额外占用5%-10%堆内存,小堆(<4GB)下性价比低。
- 大堆延迟上升:堆超过64GB后,Region数量过多,记忆集维护和筛选回收耗时增加,STW可能突破目标值。
- 调优复杂度:极端场景(如大对象频繁分配)需调整Region大小、年轻代比例等参数,否则可能触发Full GC。
3. ZGC:新一代低延迟的“突破者”
优势:
- 亚毫秒级停顿:全阶段并发(仅初始/最终标记有微秒级STW),实际停顿通常<1ms,远超G1和CMS,适合高频交易、实时数据分析等极致低延迟场景。
- 超大堆支持:最大支持16TB堆,且性能随堆大小增长衰减缓慢(大堆下优势显著)。
- 几乎无碎片:并发转移阶段动态整理对象,长期运行碎片率接近0,大对象分配无压力。
- 低CPU开销:读屏障设计轻量(额外消耗1%-3% CPU),并发阶段效率高于CMS,对吞吐量影响小。
劣势:
- 小堆性价比低:堆<10GB时,ZGC的着色指针和Region管理开销占比高,吞吐量可能低于G1。
- 分代支持较新:分代ZGC(Java 15+)对年轻代的优化不如G1成熟,短期存活对象多的场景可能略逊。
- 工具链适配滞后:部分监控工具(如早期VisualVM)对ZGC指标支持不足,需依赖JDK自带工具(jstat、jcmd)。
三、选型建议
- 旧系统维护:若基于JDK 8且堆2-10GB,可暂时保留CMS(需容忍碎片风险),但建议逐步迁移至G1。
- 中大型堆(4-64GB)+ 平衡需求:优先选择G1,默认参数即可满足多数场景(Web服务、电商系统等)。
- 超大堆(≥32GB)+ 低延迟(<1ms):必须选择ZGC(Java 17+),如分布式缓存、实时风控系统。
- 小堆(<4GB)+ 吞吐量优先:无需考虑三者,直接用ParallelGC(默认)更高效。
总结
从CMS到G1再到ZGC,JVM GC的演进本质是**“延迟、吞吐量、内存开销”的平衡艺术**:CMS以“并发”突破低延迟但牺牲了碎片和CPU;G1以“Region+混合回收”实现平衡但受限于大堆;ZGC以“着色指针+全并发”实现质的飞跃,但依赖新硬件和JDK版本。没有“最优GC”,只有“最适合场景的GC”——选型需紧扣堆大小、延迟目标和资源约束,而非盲目追新。