文章摘要(AI生成)
ZGC(Z Garbage Collector)是Java 11引入的新一代垃圾回收器,旨在解决传统GC的“延迟诅咒”问题,为大内存和低延迟场景提供高效的垃圾回收解决方案。其设计目标为最大GC停顿时间低于10毫秒,并支持TB级的堆内存。ZGC通过采用并发标记、并发复制、加载屏障和着色指针等先进技术,实现了几乎无停顿的垃圾回收机制。与传统的分代回收不同,ZGC采用单代模型,几乎完全并发处理整个堆,无需长时间的应用暂停,确保了高吞吐和低延迟。这些特性使ZGC成为对延迟敏感的应用(如电商秒杀、实时游戏、广告竞价)理想的选择。随着ZGC持续迭代,未来将推出更多优化功能,进一步提升其在复杂应用中的性能表现。
Java 的 GC 已经不再是 “Stop-The-World” 的代名词。ZGC 作为 JDK 11 引入的新一代垃圾回收器,正在彻底改变 Java 应用在大内存和低延迟场景中的表现。
🧱 垃圾回收的“延迟诅咒”
Java 的垃圾回收器(GC)机制为开发者带来了无需手动管理内存的便利,但也带来了一个长期困扰的问题——GC 暂停(GC Pause)。
随着应用复杂度的增加、堆内存规模不断扩大,传统的 GC(如 Parallel GC、CMS、甚至 G1)都难以满足以下场景:
- 电商平台秒杀,要求毫秒级响应;
- 实时游戏服务器,对卡顿零容忍;
- 广告竞价系统,高并发 + 低延迟;
- TB 级大内存数据库,希望内存越大越稳。
于是,Oracle 推出了 ZGC(Z Garbage Collector),作为一款面向未来的 GC,它旨在彻底打破延迟瓶颈。
🎯 为大内存和低延迟而生
根据 JEP 333,ZGC 的设计目标非常激进:
目标 | 说明 |
---|---|
最大 GC 停顿时间 < 10ms | 堆再大也不怕,停顿时间与堆大小无关 |
支持超大堆内存 | 支持多达 几 TB 的堆 |
高吞吐 + 高并发 | 回收阶段几乎全部并发完成 |
零碎片,压缩内存 | 不再担心 CMS 的“碎片化地狱” |
ZGC 并非“演化”,而是彻底“重构”了 GC 的运行方式。
🔥如何做到低延迟GC
ZGC 的核心算法是:
✅ 并发标记 + 并发复制(Relocation)+ Load Barrier 修复
所以,虽然它也有“标记”和“清理”阶段,但并不是真正意义上的“传统标记-清理(Mark-Sweep)”。
- 传统 Mark-Sweep:标记后不复制对象,直接清理垃圾区域。
- ZGC:标记存活对象后,复制对象到新区域,然后清理整个旧区域。
🔁 所以它更像是 Mark-Compact(标记-压缩),但压缩(即重定位)是 并发进行 的,不需要长时间暂停!
✅ ZGC 是不是对年轻代进行垃圾回收?
问题 | 回答 |
---|---|
ZGC 是否只对年轻代 GC? | ❌ 否,它是 单代 GC,不分代 |
ZGC 是否回收整个堆? | ✅ 是,回收整个堆中的垃圾对象 |
是否有专门的 minor/major GC? | ❌ 没有 minor/major 的概念 |
那 ZGC 的目标对象是什么? | 所有不可达对象,无论生命周期长短 |
🔍 为什么不分代也能做到高性能?
传统 GC 采用分代的原因是:
- 年轻对象生命周期短,回收频繁更划算;
- 老年代回收慢,尽量延迟处理。
而 ZGC 采用了以下机制,让分代变得不再必要:
技术 | 作用说明 |
---|---|
并发标记 + 并发复制 | 快速识别和移动所有活对象 |
Load Barrier | 自动处理引用修复,无需暂停 |
着色指针 | 高效标记状态,无需 side tables |
整堆并发清理 | 整体清理碎片,性能一致 |
🔬 撑起“几乎无停顿”回收的机制
🟡 并发标记(Concurrent Marking)
通过一个极短的“初始标记”暂停,ZGC 标记出 GC Roots,然后并发地追踪所有存活对象。
与传统 GC 不同,ZGC 不需要长时间 STW 来完成标记。
假设有如下对象结构:
GC Roots
└──> A
└──> B
此外还有一个已经不可达的对象 C
(垃圾):
C // 没有任何引用指向它
当ZGC 开始 GC时,会触发一个极短的 “初始标记” 暂停,识别 GC Roots,并开始并发标记:
- 对象
A
和B
被标记为“可达” - 对象
C
没有任何引用,不会被标记
🟢 并发复制(Relocation)
所有被标记为“活”的对象都会被复制到新内存区域(to-space),旧内存区域将在稍后清理。
这个搬迁过程是并发进行的,同时不会影响应用线程的执行。
在并发标记完成后,ZGC 并发地 将存活对象从旧内存区域(from-space)复制到新区域(to-space):
此过程:
- 完全并发执行(应用线程继续运行)
- 不需要 STW 停顿
- 对象一边被用一边悄悄“搬家”
🛡️ Load Barrier(加载屏障)修复
应用线程在访问对象时会触发 ZGC 的“Load Barrier”,该机制会判断:
- 如果对象还未搬迁,则立即搬迁;
- 如果对象已搬迁,则自动指向新地址;
- 应用代码无需感知对象是否移动。
这使得对象复制的过程完全透明,同时无需显式“修复引用”。
如果应用线程在复制过程中访问 A
或 B
,会触发 ZGC 的 Load Barrier,它会自动修复引用:
🎯 你可以把
loadBarrier(ref)
看成是自动加了修复逻辑的对象引用。
🗑 并发清理(Concurrent Cleanup)
标记未命中的对象(如 C
)会被并发清理:
这个阶段也完全并发完成,不影响应用线程。
✨ 着色指针(Colored Pointers)
ZGC 巧妙利用 64 位地址的高位,嵌入对象 GC 状态(如是否标记、是否搬迁),从而无需维护 bitmap 表或额外结构,大幅提高性能。
在我们示例中,它的工作流程如下:
🌈 Step 1:GC 启动,所有对象引用默认颜色为 “未标记”
A (ptr: 0x1000, mark: 0)
B (ptr: 0x2000, mark: 0)
C (ptr: 0x3000, mark: 0) ← 未被引用
🟡 Step 2:并发标记阶段中,ZGC 标记从 GC Root 可达的对象
A.mark → 1
B.mark → 1
C.mark → 0 ← 因为不可达,没有任何线程访问它,它保持未标记
🎯 这里,“着色”就体现在对象引用地址的高位标志上,例如 C 的引用仍然带着
mark=0
。
🧹 Step 3:并发清理阶段,ZGC 遍历所有堆区域,只回收“未标记”对象
if (colorBits(ptr) == 0):
// 此对象未被标记 → 可回收
free(ptr)
最终:
- A 和 B 因为被标记,保留并搬迁;
- C 的引用上没有 mark 位,ZGC 识别为“垃圾”,被直接清理。
⚔️ 性能对比:ZGC vs 传统 GC
GC 类型 | 停顿时间 | 并发能力 | 大堆支持 | 碎片问题 | 吞吐量倾向 |
---|---|---|---|---|---|
ZGC | 🟢 <10ms | 全阶段并发 | ✅ TB级 | ❌ 无碎片 | 中等偏高 |
G1 GC | 🟡 中等 | 部分并发 | ✅ 较好 | ✅ 少量 | 高 |
CMS | 🟡 中等偏高 | 标记并发 | ❌ 差 | ❌ 严重 | 中等 |
Parallel GC | 🔴 秒级 | ❌ 不并发 | ❌ 差 | ❌ 严重 | 极高 |
📌 ZGC 是目前唯一一个 “堆越大,停顿不变” 的垃圾回收器,非常适合对延迟敏感的大内存场景。
🔮未来展望:ZGC 正在快速迭代
自 JDK 11 引入以来,ZGC 已在多个版本中持续增强:
JDK 版本 | 新增特性 |
---|---|
11 | 初始引入,实验性功能 |
13 | 支持类卸载 |
15 | 转为正式支持,移除实验标签 |
16+ | 支持并发栈处理、NUMA 优化等 |
21+ | 将引入 Generational ZGC(代际) |
👉 Generational ZGC 是下一阶段演进:将现有 ZGC 拓展为年轻代 + 老年代模型,兼顾吞吐与延迟,进一步拉高性能天花板。
✍️ 写在最后
ZGC 是 Java GC 演进史上的一次范式转移:
“不再与吞吐作交换,而是让低延迟成为默认选项。”
对于开发者而言,ZGC 不需要修改代码,只需加上一句参数即可启用:
java -XX:+UseZGC MyApp
未来,随着 Generational ZGC 和更多优化的到来,ZGC 有望成为低延迟 Java 应用的默认选择。如果你正在构建延迟敏感的大型系统,不妨一试。
评论区