垃圾回收算法G1gc和Zgc的区别及重要特性总结

本文由 AI 生成,内容仅供参考,请仔细甄别。
核心设计目标对比:

  • G1 GC (Garbage-First): 主要目标是在可控的停顿时间(通常几百毫秒以内)下实现高吞吐量。它针对大内存(几十GB)进行了优化,但并非追求极低停顿。
  • ZGC (Z Garbage Collector): 核心设计目标是将停顿时间控制在极低水平(通常10ms以下,甚至亚毫秒级),且停顿时间几乎不随堆大小(可达TB级)增长而增加。吞吐量是其次要目标(虽然也在不断提升)。

核心GC过程详解 (关键区别所在):

  1. 标记阶段 (Marking):

    • G1 GC:
      • 并发标记 (Concurrent Marking): 这是G1的核心优势之一。与应用线程并发运行,遍历对象图找出存活对象。过程分多个子阶段:
        • 初始标记 (Initial Mark): STW。标记从GC Roots直接可达的对象。这个阶段通常很快,因为它只标记根的直接引用。
        • 根区域扫描 (Root Region Scanning): 并发。扫描Survivor区(属于根区域)中对象指向老年代的引用。必须在下一个Young GC开始前完成。
        • 并发标记 (Concurrent Marking): 并发。遍历整个堆的对象图,找出所有存活对象。可能与应用线程并发修改对象图产生浮动垃圾。
        • 最终标记 (Remark): STW。处理在并发标记期间应用线程产生的引用变化(使用SATB - Snapshot-At-The-Beginning算法)。需要短暂暂停应用线程来确保标记结果的准确性。
        • 清理 (Cleanup): STW (部分并发)。计算各个Region的存活对象信息,对Region进行排序(根据可回收空间和回收成本)。识别出完全空闲的Region进行回收。这一步本身有短暂的STW来准备回收,实际回收部分Region可以并发。
    • ZGC:
      • 并发标记 (Concurrent Marking): 完全并发。ZGC的标记阶段几乎完全与应用线程并发执行。关键实现机制:
        • 染色指针 (Colored Pointers): 这是ZGC的基石。它在64位对象指针(实际只用了42-48位寻址)的高位元数据位中存储标记信息(Marked0, Marked1, Remapped)和对象状态(Finalizable等)。无需修改对象头。
        • 读屏障 (Load Barrier): 这是实现全并发标记和转移的核心。 当应用线程从堆内存加载引用时,这个屏障代码会被触发。它的主要作用是检查指针的元数据位:
          • 如果发现指针指向的对象尚未被标记(或需要重映射),屏障会确保在应用代码使用该引用前,完成必要的标记或重映射操作(可能由该线程自己执行,或由GC线程完成)。
          • 这保证了应用线程看到的内存视图始终是“正确”的(要么指向旧地址但标记了,要么指向新地址),从而允许GC线程完全并发地进行标记和转移。
      • ZGC标记过程: 没有传统意义上的STW初始标记和最终标记阶段。整个标记过程通过读屏障与应用线程高度协作并发完成,避免了长时间的STW暂停。ZGC的标记阶段通常包括多个循环(Mark Start, Mark End),但都是在并发状态下完成。
  2. 转移/疏散阶段 (Evacuation/Relocation):

    • G1 GC:
      • 疏散暂停 (Evacuation Pause): STW。 这是G1产生主要停顿时间的阶段。G1根据之前的标记和Region排序结果,选择一组Region(称为Collection Set, CSet)进行回收。CSet通常包含所有年轻代Region和部分预测回收收益高的老年代Region。
      • 过程: 在STW期间,将CSet中存活的对象复制(疏散)到新的、空闲的Region中(目标可能是Survivor区或老年代Region)。同时更新所有指向这些被移动对象的引用。
      • 机制: 使用写屏障 (Write Barrier) 记录在并发标记阶段应用线程修改对象引用(称为SATB日志),确保在最终标记阶段能正确处理这些变更。转移阶段本身需要STW来原子性地完成对象的移动和引用的更新。
    • ZGC:
      • 并发转移 (Concurrent Relocation): 完全并发。 这是ZGC最革命性的特性之一。它不需要STW来移动对象。
      • 过程:
        • ZGC选择一组需要回收(通常是垃圾比例很高)的Region作为重分配集 (Relocation Set)
        • 读屏障再次成为关键: 当应用线程尝试访问位于重分配集中的对象时:
          • 读屏障会检查该对象是否已被转移。如果没有,则由当前应用线程(或GC线程)立即执行该对象的转移(复制到新的Region),并原子性地更新该引用指针(通过CAS操作)指向新地址,并设置指针的Remapped状态。
          • 同时,在旧对象位置留下一个转发指针 (Forwarding Pointer),指向新位置。
        • 其他线程访问同一对象时,读屏障要么看到Remapped状态直接使用新地址,要么通过转发指针找到新地址并尝试更新自己的引用。
      • 转移后处理 (Remapping): 在下一个标记周期中,ZGC会并发地遍历所有引用(特别是那些在转移期间没有被应用线程访问到的对象引用),利用留下的转发指针,将这些引用更新到对象的新地址(即进行重映射)。读屏障在这个过程中也会协助修复应用线程新加载的引用。

重要特性总结:

  • G1 GC:

    1. 分代收集: 逻辑上仍然区分年轻代(Eden, Survivor)和老年代,但物理上是大小相等的Region。
    2. Region分区: 堆被划分为多个固定大小(默认约1MB-32MB)的Region,回收以Region为单位。
    3. 增量回收与混合收集: 不是一次回收整个堆,而是增量地进行。混合收集既回收年轻代Region,也回收选定的老年代Region。
    4. 可预测停顿模型: 通过设置-XX:MaxGCPauseMillis(默认200ms)目标,G1尽力(但不保证)控制每次GC停顿时间在该目标内。它根据历史数据和Region信息预测选择回收收益最大的Region组成CSet。
    5. Remembered Sets (RSet): 每个Region都有一个RSet,记录其他Region指向本Region内对象的引用。用于避免在Young GC时扫描整个老年代。维护RSet是G1的重要开销来源之一。
    6. SATB算法: 处理并发标记期间引用变化的算法,以标记开始时的对象图为快照。
    7. STW阶段: 存在明确的STW阶段:初始标记、最终标记、疏散暂停(主要停顿)、清理(部分STW)。
  • ZGC:

    1. 亚毫秒级停顿目标: 首要设计目标,停顿时间通常在10ms以下,且与堆大小无关(TB级堆也能保持)。
    2. 全并发操作: 标记、转移(重定位)、重映射等关键阶段几乎都是并发执行的,极大地减少了STW。
    3. 染色指针 (Colored Pointers): 核心创新,将GC元数据存储在指针本身,无需对象头修改,减少了内存占用和访问开销。同时实现了高效的屏障检查和并发处理。
    4. 读屏障 (Load Barrier): 实现并发标记和并发转移的基石。代价是每次从堆加载引用都有额外检查(但JVM高度优化了屏障代码)。
    5. NUMA感知: 对非统一内存访问架构有良好支持,优先在本地NUMA节点分配内存,提升性能。
    6. 不分代 (Single Generation - 目前): 当前版本的ZGC(截至JDK 21)没有物理或逻辑上的分代。它统一管理整个堆。这是其追求极致低延迟的一个设计选择(避免分代间引用跟踪的复杂性和潜在停顿)。未来计划引入分代式ZGC (Generational ZGC - 预览中)。
    7. 基于Region: 堆也划分为Region(称为ZPages,大小更灵活:2MB, 32MB等),但管理方式与G1不同。
    8. 极少的STW: STW阶段非常短暂,主要用于启动GC周期(如根扫描准备)和极少数必要的全局同步点(如JVM安全点),时间通常固定且极短(与堆大小无关)。

如何选择?

  • 选择 G1 GC:

    • 堆大小在几十GB到一两百GB级别。
    • 可以容忍几百毫秒的GC停顿(通常要求 < 200ms)。
    • 追求较高的整体吞吐量。
    • 运行在JDK 8, 11等较早版本(ZGC支持可能受限或不够成熟)。
    • 应用对中等延迟敏感,但非极端要求。
  • 选择 ZGC:

    • 堆非常大(数百GB甚至TB级)。
    • 对停顿时间极度敏感,要求暂停在10ms甚至亚毫秒级(如金融交易、实时系统、大型交互式应用)。
    • 应用需要极低的服务延迟保证。
    • 运行在较新的JDK版本(JDK 15+,生产推荐JDK 17 LTS+)。
    • 愿意接受为读屏障带来的轻微吞吐量开销(与低停顿的收益相比通常是值得的)。

总结关键区别表:

特性 G1 GC ZGC
主要目标 可控停顿(几百ms)下的高吞吐量 极低停顿(<10ms),堆大小无关
堆大小适应性 大堆(几十GB) 超大堆(TB级)
分代 (逻辑分代, 物理Region) (目前统一管理,分代ZGC在预览中)
标记阶段 并发标记 + STW子阶段(初始/最终标记) 完全并发标记 (依赖读屏障)
转移/疏散阶段 STW (Evacuation Pause) 完全并发转移 (核心革命, 依赖读屏障)
关键创新 Region, RSet, SATB, 预测模型 染色指针, 读屏障
核心STW来源 初始标记, 最终标记, 疏散暂停 极短暂的根处理/安全点
停顿时间预测性 尽力控制(MaxGCPauseMillis) 高度可预测且极低
停顿与堆大小关系 随堆增大可能增加 基本无关
内存开销 中等 (RSet, Card Table) 较低 (元数据在指针)
吞吐量 中等至高 (持续优化中,略低于G1)
JDK支持 JDK 7u4+ (生产: JDK 8u20+, JDK9默认) JDK 11+ (实验), JDK15+ (生产)
posted @ 2025-06-04 22:05  MuXinu  阅读(298)  评论(0)    收藏  举报