G1 收集器原理

候选人小孙在面试字节 P7 时,面试官问道:

"G1 收集器的核心设计是什么?"

小孙说:"把堆分成多个 Region..."面试官追问:"G1 的 Young GC 和 Mixed GC 有什么区别?"

小孙答不上来。面试官继续追问:"G1 怎么实现停顿时间目标的?"

小孙彻底卡住了...

一、核心问题:G1 收集器原理 🔴

1.1 问题拆解

第一层:Region 设计(怎么分?)
  "G1 的 Region 是什么?"
  考察点:Region 划分、Humongous 区域

第二层:GC 类型(有哪些?)
  "G1 有哪几种 GC 类型?"
  考察点:Young GC / Mixed GC / Full GC

第三层:停顿时间控制(怎么控制?)
  "G1 怎么实现 MaxGCPauseMillis?"
  考察点:Mixed GC 的 Region 选择

1.2 ❌ 错误示范

候选人原话 A:"G1 不分代。"

问题诊断:G1 是逻辑分代的。它将堆划分为多个 Region,每个 Region 可以扮演不同的角色(Eden/Survivor/Old),而不是物理上的固定分代。

候选人原话 B:"G1 完全没有停顿。"

问题诊断:G1 仍然有 Stop-The-World 停顿(Young GC 和 Mixed GC),但停顿时间可控。

1.3 标准回答

P5 级别:Region 划分

G1 的 Region 设计

G1 将堆划分为多个大小相等的 Region(默认 1MB,最大 32MB):

graph TD
    subgraph G1 堆内存
        R1[Eden Region<br/>新对象分配]
        R2[Eden Region]
        R3[Survivor Region<br/>存活对象]
        R4[Old Region<br/>长期存活对象]
        R5[Humongous Region<br/>大对象(>50% Region)]
        R6[Free Region]
    end

    subgraph Remembered Set
        RS1[RS: 指向 R3 的引用]
        RS2[RS: 指向 R4 的引用]
    end

Humongous 区域

超过 Region 大小 50% 的对象被称为 Humongous 对象,存储在 Humongous Region 中。Humongous 对象的回收优先级较低。

P6 级别:GC 类型

G1 的三种 GC 类型

GC 类型触发条件回收区域停顿时间
Young GCEden 区满年轻代 Region较短
Mixed GC老年代使用率超过阈值年轻代 + 部分老年代 Region可控
Full GCMixed GC 不够全堆长(Serial Old)

Young GC

当 Eden Region 满了时触发。G1 将 Eden + Survivor Region 中的存活对象复制到新的 Survivor Region 或 Old Region。

Mixed GC

G1 在 Young GC 时,追踪老年代的垃圾比例。如果老年代 Region 中的垃圾比例较高(通过 IVO - Initial Marked Value Offset),G1 将 Young GC 扩展为 Mixed GC,同时回收部分老年代 Region。

P7 级别:停顿时间目标的实现

MaxGCPauseMillis 的实现

G1 不追求一次回收整个堆,而是通过增量回收的方式实现停顿时间目标:

-XX:MaxGCPauseMillis=200  # 期望最大停顿时间 200ms

实现原理

  1. G1 维护一个待回收 Region 列表

  2. 每次 GC 时,G1 选择回收价值最高的 Region(垃圾最多的 Region)

  3. G1 计算每个 Region 的回收收益(回收后释放的空间 / 回收成本)

  4. 选择收益最高的 Region,直到达到停顿时间目标

Cocktail Table(鸡尾酒表)

G1 使用 Cocktail Table 追踪每个 Region 的 GC 信息:

// 每个 Region 有一个 GC 信息记录
class G1RegionTable {
    int gcCount;      // GC 次数
    long gcTimeSum;   // 累计 GC 时间
    double gcTimeAvg; // 平均 GC 时间
}

通过历史数据,G1 预测下一次 GC 的时间,从而选择合适数量的 Region。

【面试官心理】 这道题我能问到 P7 级别,是因为 G1 的停顿时间控制是 GC 设计的核心技术。能说清 Cocktail Table 的候选人说明他理解了 G1 的自适应调优机制。

1.4 追问升级

追问:G1 什么时候会触发 Full GC?

G1 触发 Full GC 的情况:

  • Mixed GC 回收的速度跟不上对象分配的速度
  • Humongous 对象过多,导致没有足够的空间
  • 元空间不足

二、生产调优 🟡

2.1 G1 调优参数

# 停顿时间目标
-XX:MaxGCPauseMillis=200

# Region 大小
-XX:G1HeapRegionSize=4m  # 必须是 2 的幂次

# 启动 Mixed GC 的阈值
-XX:InitiatingHeapOccupancyPercent=45  # 堆使用率 45% 时开始并发周期

# Mixed GC 的目标 Region 数量
-XX:G1NewSizePercent=5  # 年轻代最小比例
-XX:G1MaxNewSizePercent=60  # 年轻代最大比例

2.2 G1 vs CMS

维度CMSG1
碎片化标记-清除,有碎片标记-整理,无碎片
停顿时间不可控可控(MaxGCPauseMillis)
吞吐量较低较高
内存占用Remembered Set 开销
💡

面试加分点:能说出"JDK 12 引入了 G1 的 Abortable Preclean 优化,当 Young GC 发现 Mixed GC 无法在目标停顿时间内完成时,可以中止并发清理阶段,减少无谓的 CPU 消耗",说明他关注了 JDK 新特性。

⚠️

面试陷阱:被问到"G1 的 Region 大小必须是 2 的幂次吗",很多人会说"不是"。准确答案是:必须是 2 的幂次,因为 G1 使用位运算计算 Region 索引。