以下是关于 CMS 、G1 和 ZGC 三种垃圾回收器的详细对比分析,涵盖适用场景、优势、劣势等方面。
一、CMS(Concurrent Mark-Sweep)垃圾回收器
1️⃣ 工作机制
- 初始标记 :标记 GC Roots,短暂停顿。
- 并发标记 :遍历对象图,标记可达对象。
- 重新标记 :修复并发标记期间发生的对象引用变化(短暂停顿)。
- 并发清理 :清理垃圾对象。
- 并发重置 :重置本次GC过程中的标记数据。
2️⃣ 优点
- 低延迟 :非常适合需要低延迟的应用场景,例如响应时间敏感的交互式应用。
- 并发收集 :并发标记和并发清理阶段可以和业务线程同时运行,减少停顿时间。
3️⃣ 缺点
- 内存碎片 :清理过程中不整理内存,容易导致内存碎片问题,可能引发 Full GC 。
- 单线程清理 :并发清理阶段可能效率较低。
- 高 CPU 开销 :需要与业务线程抢占 CPU 资源。
- 浮动垃圾 :无法处理浮动垃圾(在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了)
- 回收滞后触发Full GC停顿 :执行过程过程中的不确定性,会存上上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发full gc,也就是"concurrent model failure",此时会进入stop the world,用serial old垃圾收集器来回收
4️⃣ 适用场景
- 适用于低延迟需求的应用,例如 Web 服务、交易系统。
二、G1(Garbage-First)垃圾回收器
1️⃣ 工作机制
- 基于分区(Region)的回收策略,分区大小可调(通常 1MB~32MB)。
- G1 主要通过:
- 初始标记 :标记 GC Roots,可达对象。
- 并发标记 :扫描整个堆,标记可回收对象。
- 最终标记 :修复并发标记期间发生的对象变化。
- 筛选回收 :根据分区的垃圾比例优先回收高收益区域。
2️⃣ 优点
- 并行与并发 :G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者核心)来缩短Stop-The-World停顿时间。部分其他收集器需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行
- 分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但还是保留了分代的概念。
- 空间整合:与CMS的“标记-整理”算法不同,G1从整体来看是基于“标记整理”算法算法实现的收集器;从局部上来来看是基于"标记整理"算法来实现的收集器;从局部上来看是基于“复制”算法实现的。
- 可预测停顿时间 :可以通过
-XX:MaxGCPauseMillis
设置目标停顿时间。 - 高吞吐量 :分区化管理内存,提高内存利用率。
3️⃣ 缺点
- 配置复杂 :需要较多的调优工作以达到最佳性能。
- 吞吐量略低 :相比 Parallel GC,在高吞吐量场景稍逊。
4️⃣ 适用场景
- 内存管理复杂且需要低延迟的场景,例如实时分析系统、大型 Java 应用。
- 50%以上的堆被存活对象占用
- 对象分配和晋升的速度变化非常大
- 垃圾回收时间特别长,超过1秒
- 8GB以上的堆内存(建议值)
- 停顿时间是500ms以内
三、ZGC(Z Garbage Collector)
1️⃣ 工作机制
- 基于Region内存布局
- 暂时不分代
- 以 低延迟 为目标,设计为 完全并发 的垃圾回收器。
- 支持超大堆(最高 16TB),采用 读屏障和颜色指针技术来实现可并发的标记-整理算法。
- ZGC通过:
- 并发标记:与G1一样,并发标记是遍历对象图做可达性分析的阶段,它的初始标记(Mark Start)和最终标记(Mark End)也会出现短暂的停顿,与G1不同的是,ZGC的标记是在指针上而不是在对象上进行的,标记阶段会更新颜色指针中的Marked 0、Marked 1标记位。
- 并发预备重分配:这个阶段要根据特定的查询条件统计得出本次收集过程要清理哪些Region,将这些Region组成重分配集(Relocation Set)。ZGC每次回收都会扫描所有的Region,用范围更大的扫描成本换取省去G1中记忆集的维护成本。
- 并发重分配:重分配是ZGC执行过程中的核心阶段,这个过程要把重分配的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表(Forward Table),记录从旧对象到新对象的转向关系。ZGC收集器能仅从引用上就明确得知一个对象是否处于重分配集之中,如果用户线程并发访问了位于重分配集中的对象,这次访问将会被预置的内存屏障(读屏障)所截获,然后立即根据Region上转发表记录将访问转发到新复制的对象上,并同时修正更新该引用的值,使其直接指向新对象,ZGC将这种行为称为指针的治愈(Self-Healing)能力。
- 并发重映射:重映射所做的就是修正整个堆中指向重分配集中旧对象的所有引用,但是ZGC中对象引用存在“自愈”功能,所以这个重映射操作并不很迫切。ZGC很巧妙地把并发重映射阶段要做的工作,合并到下一次垃圾收集循环中的并发标记阶段里去完成,反正它们都是要遍历所有对象的,这样合并就节省了一次遍历对象图的开销。一旦所有指针都被修正之后,原来记录新旧对象关系的转发表就可以释放掉了。
2️⃣ 优点
- 极低停顿时间 :单次停顿通常不超过 10ms。
- 大堆支持 :针对超大内存的应用进行了优化。
- 无内存碎片 :通过并发整理内存,保持内存连续性。
3️⃣ 缺点
- 相对较高的 CPU 开销 :需要额外的 CPU 资源处理读屏障。
- 浮动垃圾
- 新生代管理能力稍弱 :相比 G1,处理短生命周期对象略显不足。
- 较高的 JVM 版本要求 :需要 Java 11+。
4️⃣ 适用场景
- 超大堆应用(如 8GB+ 内存)。
- 低延迟要求非常高的场景,例如金融交易系统、在线游戏。
四、对比总结
特性 | CMS | G1 | ZGC |
目标 | 降低延迟 | 平衡延迟与吞吐量 | 极低延迟 |
停顿时间 | 较低,但存在 Full GC 风险 | 可控停顿时间(可调) | 极低(<10ms) |
内存管理 | 无内存整理,易碎片化 | 分区压缩,碎片较少 | 并发整理,无碎片 |
吞吐量 | 高 | 较高 | 较低 |
适用场景 | 低延迟的中小型应用 | 内存利用率优化的大型应用 | 超大堆低延迟应用 |
支持 JVM | Java 8+ | Java 9+ | Java 11+ |