Java中的G1垃圾回收器详解
字数 2289 2025-11-11 16:26:56

Java中的G1垃圾回收器详解

一、G1垃圾回收器概述

G1(Garbage-First)是JDK 7 Update 4中正式引入的服务器端垃圾回收器,旨在替代CMS回收器,解决其内存碎片化和停顿时间不可控等问题。G1的设计目标是在延迟可控的情况下获得尽可能高的吞吐量,适用于多核处理器、大内存容量的服务端环境。它的核心思想是将堆内存划分为多个大小相等的独立区域(Region),并跟踪各个区域的垃圾堆积程度,优先回收垃圾最多的区域(Garbage-First名称的由来)。

二、G1的核心设计思想

  1. Region化内存布局

    • G1将整个堆划分为2048个左右的等大小区域(Region),每个Region的大小可以是1MB到32MB(必须是2的幂次)。
    • 区域类型包括:
      • Eden区:存放新创建的对象。
      • Survivor区:存放年轻代GC后存活的对象。
      • Old区:存放长期存活的对象。
      • Humongous区:专门存放大对象(大小超过Region容量50%的对象)。
    • 优势:Region是GC过程中每次回收的最小单元,避免了全堆扫描,使得GC停顿时间更可控。
  2. 分代收集的弱化

    • G1虽然保留了年轻代和老年代的概念,但物理上不再连续,而是由一系列不连续的Region组成。
    • 年轻代和老年代的比例不再固定,而是动态调整(默认年轻代占比5%~60%),通过-XX:G1NewSizePercent-XX:G1MaxNewSizePercent参数控制。
  3. 回收策略:化整为零

    • G1的回收过程不再是整代回收,而是每次只回收部分Region(称为"回收集"或Collection Set)。
    • 通过计算每个Region的回收价值(回收所需时间与可释放空间的比值),优先回收收益最大的Region。

三、G1的工作流程

  1. 年轻代GC(Young GC)

    • 触发条件:Eden区被占满时。
    • 过程:
      • 暂停所有应用线程(Stop-The-World)。
      • 从根对象(如栈引用、全局变量)开始扫描,标记Eden和Survivor区中存活的对象。
      • 将存活对象复制到新的Survivor区或晋升到Old区。
      • 清空Eden和已回收的Survivor区。
    • 特点:并行执行,使用多线程加速回收。
  2. 并发标记周期(Concurrent Marking Cycle)

    • 触发条件:堆内存使用率达到阈值(默认45%,通过-XX:InitiatingHeapOccupancyPercent设置)。
    • 阶段划分:
      • 初始标记(Initial Mark):短暂STW,标记从GC Roots直接可达的对象。
      • 根区域扫描(Root Region Scanning):并发扫描Survivor区中引用老年代的对象。
      • 并发标记(Concurrent Marking):并发遍历整个堆,标记存活对象。
      • 最终标记(Remark):短暂STW,处理并发标记期间漏标的对象(使用SATB算法)。
      • 清理(Cleanup):统计各Region存活对象比例,识别完全空闲的Region,准备混合GC。
  3. 混合GC(Mixed GC)

    • 触发条件:并发标记周期完成后。
    • 回收目标:不仅回收年轻代Region,还会选择部分老年代Region(根据回收价值)加入回收集。
    • 过程:类似年轻代GC,但回收范围包括年轻代和部分老年代。
    • 目标:逐步减少老年代占用,避免Full GC。

四、关键技术与优化

  1. SATB(Snapshot-At-The-Beginning)算法

    • 在并发标记开始时,对堆内存存活对象建立快照。
    • 期间新分配的对象默认视为存活,避免漏标。
    • 通过写前屏障(Write Barrier)记录并发标记期间被修改的引用,在最终标记阶段重新扫描。
  2. 记忆集(Remembered Set)与卡表(Card Table)

    • 每个Region维护一个记忆集,记录其他Region指向本Region的引用。
    • 避免全堆扫描,只需扫描记忆集即可确定跨代引用。
    • 卡表是记忆集的实现方式,将堆划分为512字节的卡页,通过写屏障维护脏卡页。
  3. 停顿预测模型

    • G1根据历史的GC数据预测本次回收的停顿时间。
    • 通过调整回收集的大小(Region数量),尽量满足用户设定的最大停顿时间(-XX:MaxGCPauseMillis,默认200ms)。

五、G1的调优参数

  1. 基础参数

    • -XX:+UseG1GC:启用G1回收器。
    • -XX:MaxGCPauseMillis=200:设置最大停顿时间目标。
    • -XX:G1HeapRegionSize=16m:手动指定Region大小。
  2. 并发标记控制

    • -XX:InitiatingHeapOccupancyPercent=45:触发并发标记的老年代占用阈值。
  3. 并行线程数

    • -XX:ParallelGCThreads:设置STW阶段并行线程数(通常为CPU核数)。

六、G1的适用场景与局限性

  • 适用场景

    • 堆内存较大(如6GB以上)的应用。
    • 要求低延迟(如停顿时间低于500ms)的服务。
    • 应对堆内存占用率随时间增长的情况。
  • 局限性

    • 内存占用较高(记忆集和卡表需额外空间)。
    • 写屏障开销可能影响吞吐量。
    • 小堆环境下优势不明显。

七、总结
G1通过Region化布局和回收价值优先策略,实现了可预测的停顿时间与高吞吐量的平衡。其并发标记和混合GC机制有效减少了Full GC的发生,适合现代大内存应用。理解其核心原理和调优方法,有助于在实际生产中合理配置,发挥最大性能。

Java中的G1垃圾回收器详解 一、G1垃圾回收器概述 G1(Garbage-First)是JDK 7 Update 4中正式引入的服务器端垃圾回收器,旨在替代CMS回收器,解决其内存碎片化和停顿时间不可控等问题。G1的设计目标是在延迟可控的情况下获得尽可能高的吞吐量,适用于多核处理器、大内存容量的服务端环境。它的核心思想是将堆内存划分为多个大小相等的独立区域(Region),并跟踪各个区域的垃圾堆积程度,优先回收垃圾最多的区域(Garbage-First名称的由来)。 二、G1的核心设计思想 Region化内存布局 : G1将整个堆划分为2048个左右的等大小区域(Region),每个Region的大小可以是1MB到32MB(必须是2的幂次)。 区域类型包括: Eden区:存放新创建的对象。 Survivor区:存放年轻代GC后存活的对象。 Old区:存放长期存活的对象。 Humongous区:专门存放大对象(大小超过Region容量50%的对象)。 优势:Region是GC过程中每次回收的最小单元,避免了全堆扫描,使得GC停顿时间更可控。 分代收集的弱化 : G1虽然保留了年轻代和老年代的概念,但物理上不再连续,而是由一系列不连续的Region组成。 年轻代和老年代的比例不再固定,而是动态调整(默认年轻代占比5%~60%),通过 -XX:G1NewSizePercent 和 -XX:G1MaxNewSizePercent 参数控制。 回收策略:化整为零 : G1的回收过程不再是整代回收,而是每次只回收部分Region(称为"回收集"或Collection Set)。 通过计算每个Region的回收价值(回收所需时间与可释放空间的比值),优先回收收益最大的Region。 三、G1的工作流程 年轻代GC(Young GC) : 触发条件:Eden区被占满时。 过程: 暂停所有应用线程(Stop-The-World)。 从根对象(如栈引用、全局变量)开始扫描,标记Eden和Survivor区中存活的对象。 将存活对象复制到新的Survivor区或晋升到Old区。 清空Eden和已回收的Survivor区。 特点:并行执行,使用多线程加速回收。 并发标记周期(Concurrent Marking Cycle) : 触发条件:堆内存使用率达到阈值(默认45%,通过 -XX:InitiatingHeapOccupancyPercent 设置)。 阶段划分: 初始标记(Initial Mark) :短暂STW,标记从GC Roots直接可达的对象。 根区域扫描(Root Region Scanning) :并发扫描Survivor区中引用老年代的对象。 并发标记(Concurrent Marking) :并发遍历整个堆,标记存活对象。 最终标记(Remark) :短暂STW,处理并发标记期间漏标的对象(使用SATB算法)。 清理(Cleanup) :统计各Region存活对象比例,识别完全空闲的Region,准备混合GC。 混合GC(Mixed GC) : 触发条件:并发标记周期完成后。 回收目标:不仅回收年轻代Region,还会选择部分老年代Region(根据回收价值)加入回收集。 过程:类似年轻代GC,但回收范围包括年轻代和部分老年代。 目标:逐步减少老年代占用,避免Full GC。 四、关键技术与优化 SATB(Snapshot-At-The-Beginning)算法 : 在并发标记开始时,对堆内存存活对象建立快照。 期间新分配的对象默认视为存活,避免漏标。 通过写前屏障(Write Barrier)记录并发标记期间被修改的引用,在最终标记阶段重新扫描。 记忆集(Remembered Set)与卡表(Card Table) : 每个Region维护一个记忆集,记录其他Region指向本Region的引用。 避免全堆扫描,只需扫描记忆集即可确定跨代引用。 卡表是记忆集的实现方式,将堆划分为512字节的卡页,通过写屏障维护脏卡页。 停顿预测模型 : G1根据历史的GC数据预测本次回收的停顿时间。 通过调整回收集的大小(Region数量),尽量满足用户设定的最大停顿时间( -XX:MaxGCPauseMillis ,默认200ms)。 五、G1的调优参数 基础参数 : -XX:+UseG1GC :启用G1回收器。 -XX:MaxGCPauseMillis=200 :设置最大停顿时间目标。 -XX:G1HeapRegionSize=16m :手动指定Region大小。 并发标记控制 : -XX:InitiatingHeapOccupancyPercent=45 :触发并发标记的老年代占用阈值。 并行线程数 : -XX:ParallelGCThreads :设置STW阶段并行线程数(通常为CPU核数)。 六、G1的适用场景与局限性 适用场景 : 堆内存较大(如6GB以上)的应用。 要求低延迟(如停顿时间低于500ms)的服务。 应对堆内存占用率随时间增长的情况。 局限性 : 内存占用较高(记忆集和卡表需额外空间)。 写屏障开销可能影响吞吐量。 小堆环境下优势不明显。 七、总结 G1通过Region化布局和回收价值优先策略,实现了可预测的停顿时间与高吞吐量的平衡。其并发标记和混合GC机制有效减少了Full GC的发生,适合现代大内存应用。理解其核心原理和调优方法,有助于在实际生产中合理配置,发挥最大性能。