侧边栏壁纸
  • 累计撰写 231 篇文章
  • 累计创建 70 个标签
  • 累计收到 5 条评论

目 录CONTENT

文章目录

揭秘ZGC:下一代低延迟垃圾收集器

橙序员
2025-07-13 / 0 评论 / 0 点赞 / 170 阅读 / 2,331 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(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,并开始并发标记:

image-1752375216314

  • 对象 AB 被标记为“可达”
  • 对象 C 没有任何引用,不会被标记

🟢 并发复制(Relocation)

所有被标记为“活”的对象都会被复制到新内存区域(to-space),旧内存区域将在稍后清理。

这个搬迁过程是并发进行的,同时不会影响应用线程的执行。

在并发标记完成后,ZGC 并发地 将存活对象从旧内存区域(from-space)复制到新区域(to-space):

image-1752375206315

此过程:

  • 完全并发执行(应用线程继续运行)
  • 不需要 STW 停顿
  • 对象一边被用一边悄悄“搬家”

🛡️ Load Barrier(加载屏障)修复

应用线程在访问对象时会触发 ZGC 的“Load Barrier”,该机制会判断:

  • 如果对象还未搬迁,则立即搬迁;
  • 如果对象已搬迁,则自动指向新地址;
  • 应用代码无需感知对象是否移动。

这使得对象复制的过程完全透明,同时无需显式“修复引用”。

如果应用线程在复制过程中访问 AB,会触发 ZGC 的 Load Barrier,它会自动修复引用:

image-1752375196392

🎯 你可以把 loadBarrier(ref) 看成是自动加了修复逻辑的对象引用。


🗑 并发清理(Concurrent Cleanup)

标记未命中的对象(如 C)会被并发清理:

image-1752375187048

这个阶段也完全并发完成,不影响应用线程。

✨ 着色指针(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 应用的默认选择。如果你正在构建延迟敏感的大型系统,不妨一试。

0

评论区

欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

订阅shiker.tech

文章发布订阅~

通过邮箱订阅文章更新,您将在文章发布时收到及时的邮件提醒~