新型回收器(Epsilon/Shenandoah/ZGC)
新一代 GC 的崛起
JDK 11 之后,JVM 引入了三个革命性的垃圾回收器:Epsilon、Shenandoah 和 ZGC。它们的共同特点是超低停顿,解决了传统 GC 无法解决的长停顿问题。
Epsilon GC:不做任何回收
Epsilon 是什么
Epsilon 是「无操作 GC」——它分配内存,但不回收任何垃圾。当内存耗尽时,直接 OOM。
bash
# 启用 Epsilon GC
java -XX:+UseEpsilonGC MyApp使用场景
Epsilon 只适合特定场景:
| 场景 | 说明 |
|---|---|
| 极短生命周期的程序 | 程序运行时间比 GC 停顿时间还短 |
| 测试内存分配器 | 用于测试其他内存管理机制 |
| 内存足够且不产生垃圾 | 嵌入式、实时系统 |
| 性能基准测试 | 排除 GC 干扰的基准测试 |
Epsilon 的特点
| 特点 | 说明 |
|---|---|
| 零停顿 | 不进行任何垃圾回收,无 STW |
| 无内存回收 | 分配直到 OOM |
| 分配器效率高 | 无任何 GC 开销 |
| 仅分配 | 适合不需要 GC 的场景 |
Epsilon 的配置
bash
# Epsilon + 最大堆
java -Xmx2g -XX:+UseEpsilonGC MyApp
# 注意:Epsilon 不处理垃圾,所以不要设置过小的堆Shenandoah GC:转发指针实现并发压缩
Shenandoah 是什么
Shenandoah 是 JDK 12 引入的低延迟 GC,核心目标是极短的停顿时间。
bash
# JDK 12+ 启用 Shenandoah
java -XX:+UseShenandoahGC MyApp转发指针(Brooks Pointer)
Shenandoah 的核心技术是转发指针(Brooks Pointer):
传统 GC 的对象引用:
引用 ──→ 对象 A
Shenandoah 的对象引用:
引用 ──→ 对象 A(副本)──→ 原始对象 A
↑
│
转发指针(Brooks Pointer)
对象移动后:
引用 ──→ 对象 A(副本)───修改转发指针──→ 原始对象 A(已移动)
↑
│
引用不变,只需修改转发指针转发指针的原理
当 Shenandoah 需要压缩堆时:
压缩前: 压缩后:
┌─────────────┐ ┌─────────────┐
│ Region A │ │ Region A │
│ 引用 ──→ [A]│ │ 引用 ──→ [A] ──修改──→ (A 已移动)
└─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ Region B │
│ [A] │
└─────────────┘
引用不需要改变,只需要修改 A 的转发指针
→ GC 可以在并发时压缩,不需要 STW读屏障
Shenandoah 使用读屏障(Load Barrier)来维护转发指针的一致性:
java
// 读屏障的工作:
Object obj = ref.get(); // 读取引用
// 隐式地,JVM 检查 obj 的转发指针是否有效
// 如果对象被移动了,更新到新位置读屏障的开销很小(约 5%~10%),但换来了极短的停顿时间。
Shenandoah vs G1
| 维度 | Shenandoah | G1 |
|---|---|---|
| 停顿时间 | < 10ms(并发压缩) | 数十毫秒 |
| 压缩方式 | 并发压缩(不 STW) | STW 时压缩 |
| 堆大小影响 | 几乎不受影响 | 堆越大停顿越长 |
| JDK 版本 | 12+(JDK 15 红帽移出 OpenJDK) | 7+(JDK 9 默认) |
| 算法 | 转发指针 + 读屏障 | SATB + Region |
ZGC:着色指针实现亚毫秒停顿
ZGC 是什么
ZGC(Z Garbage Collector) 是 JDK 11 引入、JDK 15 生产就绪、JDK 21 成为默认的超低延迟 GC。
bash
# JDK 11~14:启用 ZGC
java -XX:+UseZGC MyApp
# JDK 15+:默认 ZGC 已经可用
# JDK 21+:ZGC 成为默认 GC着色指针(Colored Pointers)
ZGC 使用 着色指针(Colored Pointers) 技术,把 GC 信息编码到对象的引用中:
64 位引用 → 64 位地址空间中,ZGC 只使用低 42 位作为地址
高位存储 GC 元数据:
┌──────────┬──────────┬──────────────────────┐
│ 44~46 │ 47 │ 0~44 │
│ (GC状态) │ (大堆标志)│ (实际地址) │
└──────────┴──────────┴──────────────────────┘ZGC 使用 3 个 GC 位存储对象的状态:
| 位 | 含义 |
|---|---|
| Marked0/Marked1 | 对象存活(用于并发标记) |
| Remapped | 引用已更新(对象已移动) |
ZGC 的工作过程
ZGC 完全在颜色指针的帮助下,实现了几乎所有阶段的并发:
ZGC 阶段:
1. 初始标记 ── STW(短暂)─── 标记 GC Roots 直接引用的对象
↓
2. 并发标记 ── 并发 ────────── 标记所有存活对象(着色指针)
↓
3. 再标记 ── STW(极短)─────── 修正着色指针
↓
4. 并发准备 ── 并发 ────────── 计算回收价值
↓
5. 并发回收 ── 并发 ────────── 移动存活对象,更新引用
↓
6. 并发重映射 ── 并发 ──────── 更新所有引用ZGC 的优势
| 优势 | 说明 |
|---|---|
| 停顿时间固定 | 不超过 1ms,与堆大小无关 |
| 吞吐量损失小 | 约 10%~15% 吞吐量损失 |
| 堆大小无关 | 支持从 MB 到 TB 的堆 |
| 并发压缩 | 对象移动时不需要 STW |
| 彩色指针 | 不需要 Card Table/RSet |
ZGC 的局限性
| 局限性 | 说明 |
|---|---|
| 吞吐量 | 不如 Parallel GC(高吞吐) |
| 不支持类卸载 | JDK 15+ 已支持 |
| 堆大小 < 16MB | 需要对 44 位地址空间利用 |
| 暂不支持 JDK 8 | 需要 JDK 11+ |
ZGC 的参数
bash
# 基本使用
java -XX:+UseZGC -Xms16g -Xmx16g MyApp
# 设置停顿时间目标(软目标)
java -XX:+UseZGC -XX:MaxGCPauseMillis=1ms MyApp
# 设置并发 GC 线程数
java -XX:+UseZGC -XX:ConcGCThreads=8 MyApp三种新型 GC 的对比
| 维度 | Epsilon | Shenandoah | ZGC |
|---|---|---|---|
| 停顿时间 | 0(无 GC) | < 10ms | < 1ms |
| 吞吐量 | 最高 | 中 | 中 |
| 堆大小 | 任意 | 任意 | 任意(最大 TB 级) |
| 并发 | 无 | 部分并发 | 完全并发 |
| 压缩 | 无 | 并发压缩 | 并发压缩 |
| JDK 版本 | 11+ | 12+(OpenJDK) | 11+(15+生产就绪) |
| JDK 21 | 21+ | 不在 OpenJDK | 默认 GC |
选型建议
| 场景 | 推荐 GC | 原因 |
|---|---|---|
| 超低延迟(< 1ms) | ZGC | 亚毫秒停顿 |
| 低延迟(< 10ms) | ZGC / G1 | ZGC 更稳定 |
| 高吞吐量批处理 | Parallel GC | 吞吐量优先 |
| 无垃圾程序 | Epsilon | 不做 GC |
| JDK 8 升级 | G1 | JDK 9+ |
| TB 级大堆 | ZGC | 支持超大堆 |
本节小结
三种新型 GC 的核心特点:
| GC | 核心技术 | 停顿时间 | 吞吐量 |
|---|---|---|---|
| Epsilon | 无操作 | 0 | 最高 |
| Shenandoah | 转发指针 + 读屏障 | < 10ms | 中 |
| ZGC | 着色指针 | < 1ms | 中 |
ZGC 是现代 JVM GC 的巅峰——它解决了 GC 停顿时间与堆大小无关的问题,是 JDK 21 的默认 GC。
下一节,我们来看 7 种回收器总结与调优建议。
