G1 概述/优势/适用场景
G1:重新定义垃圾回收
G1(Garbage-First) 是 JDK 7 引入、JDK 9 成为默认的垃圾回收器。它不是简单地把堆分成年轻代和老年代,而是把整个堆划分为多个大小相等的 Region,通过优先回收价值最高的 Region,实现可预测停顿时间。
G1 的核心设计
Region 划分
G1 把整个堆划分成多个大小相等的 Region(每个 1MB~32MB):
G1 堆结构:
┌──────────┬──────────┬──────────┬──────────┐
│ Eden │ Eden │ Survivor│ Old │
│ Region │ Region │ Region │ Region │
├──────────┼──────────┼──────────┼──────────┤
│ Old │ Old │Humongous │ Free │
│ Region │ Region │ Region │ Region │
├──────────┴──────────┴──────────┴──────────┤
│ G1HeapRegionSize │
│ (可配置:1MB, 2MB, 4MB, 8MB...) │
└─────────────────────────────────────────────┘G1 的 Region 类型
| Region 类型 | 说明 |
|---|---|
| Eden Region | 年轻代,存储新分配的对象 |
| Survivor Region | 年轻代,存储经历过 MinorGC 的存活对象 |
| Old Region | 老年代,存储长期存活的对象 |
| Humongous Region | 存储大对象(超过 Region 大小 50%) |
| Free Region | 空闲区域,可分配 |
G1 的分代 vs 传统分代
传统分代(Serial/Throughput/CMS):
┌──────────────────┐ ┌──────────────────┐
│ 年轻代 │ │ 老年代 │
│ Eden │ S0 │ S1 │ │ │
│ (连续区域) │ │ (连续区域) │
└──────────────────┘ └──────────────────┘
G1 分代:
┌────────┬──────┬──────┬────────┬────────┐
│ Eden │ S0 │ S0 │ Old │ Old │
│ (Region)│(Reg) │(Reg) │(Reg) │(Reg) │
├────────┴──────┴──────┴────────┴────────┤
│ 堆被划分为多个 Region │
│ 各类型 Region 不必连续 │
└─────────────────────────────────────────────┘G1 的工作过程
年轻代收集(Young GC)
G1 年轻代收集:
1. 分配新对象到 Eden Region
↓
2. Eden Region 满
↓
3. 选定所有 Eden Region 作为收集集合(CSet)
↓
4. 多线程并行复制到 Survivor Region
↓
5. 存活对象年龄达标 → 晋升到 Old Region混合收集(Mixed GC)
当老年代 Region 达到阈值时,G1 执行混合收集:
G1 混合收集:
1. 选定所有年轻代 Region + 若干老年代 Region
↓
2. CSet(收集集合)中所有存活对象被复制/清除
↓
3. 存活对象复制到 Survivor/Old Region
↓
4. 被清空的 Region 变成 Free RegionG1 的核心优势
1. 可预测停顿时间
G1 可以设置目标停顿时间:
bash
# 设置目标最大停顿时间 200ms
java -XX:MaxGCPauseMillis=200 -XX:+UseG1GC MyAppG1 会尽量满足这个目标——优先收集垃圾最多的 Region。
2. 空间整合
G1 不像 CMS 那样产生碎片。每次收集后,被清空的 Region 变成 Free Region,可以立即分配新对象:
G1 收集后:
┌────────┬────────┬────────┬────────┬────────┐
│ Free │ Free │ Old │ Free │ Eden │
│ (Reg) │ (Reg) │ (Reg) │ (Reg) │ (Reg) │
└────────┴────────┴────────┴────────┴────────┘
↓ 新对象可以直接分配到 Free Region3. 停顿时间预测
G1 通过 Remembered Set 追踪 Region 间的引用,可以精确预测每个 Region 的回收价值:
回收价值计算:
价值 = (可回收内存量) / (回收所需时间)
G1 优先收集价值最高的 Region:
- 垃圾多的 Region → 回收空间大 → 价值高
- 回收快的 Region → 所需时间少 → 价值高4. 不需要连续空间
G1 的 Region 不需要物理连续——这解决了 CMS 和 Serial Old 的碎片问题。
G1 vs 其他收集器
| 维度 | Serial | Parallel | CMS | G1 |
|---|---|---|---|---|
| 停顿时间 | 很长 | 长 | 短 | 可预测 |
| 吞吐量 | 低 | 高 | 中 | 中 |
| 内存碎片 | 无 | 无 | 有碎片 | 无碎片 |
| 大堆支持 | 一般 | 一般 | 差 | 好 |
| 可预测性 | 差 | 差 | 差 | 好 |
| JDK 默认 | - | - | - | JDK 9+ |
G1 的适用场景
| 场景 | 推荐原因 |
|---|---|
| JDK 9+ 新项目 | 默认 GC,开箱即用 |
| 追求低延迟 | 可设置目标停顿时间 |
| 大堆(4GB+) | Region 化设计,不受堆大小影响 |
| 服务延迟敏感 | G1 的停顿时间可控 |
| 避免碎片 | G1 每次收集后整合空间 |
G1 的局限性
| 局限性 | 说明 |
|---|---|
| 吞吐量不如 Parallel GC | G1 优先低延迟,吞吐量略有牺牲 |
| 大对象分配 | Humongous Region 分配效率低 |
| 内存占用 | Remembered Set 有额外开销 |
| 参数调优复杂 | 需要理解多个参数 |
本节小结
G1 收集器的核心要点:
| 维度 | 说明 |
|---|---|
| 核心思想 | 把堆分成多个 Region,优先回收价值最高的 Region |
| 停顿控制 | 设置 -XX:MaxGCPauseMillis=200 目标停顿时间 |
| 空间整合 | 无碎片,每次收集后 Region 变成 Free |
| 分代方式 | 逻辑分代(不强制 Eden/Survivor/Old 连续) |
| 适用场景 | JDK 9+ 默认GC、低延迟服务、大堆 |
| 缺点 | 吞吐量不如 Parallel GC |
G1 是现代 JVM 的标准 GC,是大多数场景下的最佳选择。
下一节,我们来看 G1 参数设置/Region/记忆集。
