当前位置: 首页 > news >正文

低延迟垃圾收集器:挑战“不可能三角”

在开始之前,必须再次强调 “不可能三角”内存占用、吞吐量、延迟,三者难以同时完美。

传统的垃圾收集器(如 Serial, Parallel, CMS, G1)在堆内存变大时,停顿时间(Latency)也会显著变长,因为它们总有一些阶段需要“Stop The World”(挂起所有用户线程)来完成清理工作。这对于需要快速响应的应用(如实时交易、大数据平台、微服务)是无法接受的。

Shenandoah 和 ZGC 的目标就是打破这个魔咒,实现在任何堆大小下(比如 4TB),停顿时间都能被严格控制在十毫秒以内,同时尽可能减少对吞吐量的影响。

它们实现这一目标的核心理念是:将最耗时的“对象移动”阶段也并发化。这听起来简单,但实现起来极其复杂,因为在你移动对象的同时,用户线程可能正在读写它。两者解决方案不同,但都极其精妙。


Shenandoah: “搬家队长”与“转发牌”

你可以把 Shenandoah 想象成一个高效的搬家队长,它的核心绝招是 “转发指针”(Brooks Pointer)

1. 它是谁?
  • 由 RedHat 开发,是 OpenJDK 的“养子”,OracleJDK 中不包含它

  • 你可以把它看作是 G1 的激进并发版,架构和 G1 很像(基于 Region 的堆布局),共享部分代码。

2. 它如何实现并发搬家?(核心原理)

想象一下,你要给一个正在营业的超市换货架,顾客还在不停买东西。你怎么做?

Shenandoah 的解决方案是:给每个商品(对象)挂上一个“转发牌”

  1. 准备工作:在每个对象内部,额外开辟一个小空间(在对象头前),里面存着一个地址。正常情况下,这个地址指向对象自己,就像商品上挂着一个写着“我在这里”的牌子。

  2. 开始搬家(并发回收阶段):Shenandoah 启动一个并发线程,悄悄地把需要回收的区域(Region)里的存活对象复制到新区域。这整个过程是不停止营业(用户线程)的

  3. 更新地址牌:对象复制完成后,Shenandoah 只做一件事:修改旧对象上的那个“转发牌”,把上面的地址从“旧对象”改为“新对象”的地址。注意:它不会去更新所有指向这个旧对象的引用

  4. 顾客访问(读/写屏障):这就是关键所在!Shenandoah 在 JVM 中设置了“哨兵”(读屏障和写屏障)。每当用户线程(顾客)要访问一个对象时,哨兵会先拦截这次访问,检查一下这个对象上的“转发牌”。

    如果牌子指向自己:说明没搬过家,直接访问。

    如果牌子指向别处:说明这个对象是旧对象,已经搬走了。哨兵会自动地、悄悄地根据牌子上的新地址,去访问新对象,并把这次访问的结果返回给用户。同时,它还会顺手把这个引用本身的值更新成新地址(只有第一次需要转发,后续就直接访问新对象了)。这个过程对用户线程是完全透明的。

为什么能控制停顿?因为最耗时的“复制对象”和“更新引用”工作都被并发线程和“哨兵”(屏障)分担了。那些必需的短暂停顿(初始标记、最终标记等)只处理少量核心信息(GC Roots),与堆大小无关,所以非常短。

3. 优缺点
  • 优点:停顿时间极短,与堆大小脱钩。

  • 缺点“哨兵”(尤其是读屏障)带来的开销非常大。因为每一次对象读取操作都要经过这个检查步骤。这导致 Shenandoah 的吞吐量损失通常是三者中最大的


ZGC: “魔法指针”与“自愈能力”

ZGC 的思路更加科幻。它不像 Shenandoah 那样给对象挂牌子,而是直接给指针(内存地址)施了魔法,它的核心是 染色指针(Colored Pointer)

1. 它是谁?
  • 由 Oracle 亲儿子开发,血统纯正,OpenJDK 和 OracleJDK 都包含。

  • 它的设计理念源自传说中的 Azul C4 收集器,非常前沿。

2. 它如何实现并发搬家?(核心原理)

继续用超市搬家的比喻。ZGC 的做法不是挂牌子,而是它有一种“魔法墨水”,可以直接在写有商品位置的导购图(指针)上做标记

  1. 魔法墨水:在 64 位系统中,我们其实用不了那么大的地址空间。ZGC 巧妙地利用了指针中未使用的比特位(比如高 4 位)来存储信息。这些信息包括:对象是否被标记、是否属于待回收集合、是否已被移动过。所以,ZGC 的指针不仅仅是地址,它本身就是携带元数据的。通过这个指针,ZGC 不用访问对象就能知道它的状态。

  2. 地址重映射(魔法地图):光在指针上写墨水,CPU 可不认账,它会把这些位也当成地址的一部分,会找错地方。ZGC 的解决方案是 多重映射:它通过操作系统的内存管理功能,将好几块不同的虚拟内存地址空间(比如,指针标志位是 0010 的地图和 0000 的地图)都映射到同一块真实的物理内存上。这样,无论指针上的“魔法墨水”怎么写,最终都能通过这张“魔法地图”找到正确的物理对象。

  3. 并发搬家与自愈:当 ZGC 要移动一个对象时:

    它并发地将对象复制到新 Region。

    它在旧对象的位置上留下一个“转发地址”(类似于转发表)。

    最关键的一步来了:当用户线程试图访问一个已经被移动的旧对象时,ZGC 的“哨兵”(读屏障)会被触发。这个哨兵一看指针上的“魔法墨水”(标志位),就知道“哦,这个对象搬走了”。于是它:去旧位置上的“转发地址”里找到新地址。直接把这个线程手中的指针值更新成新地址(并修正标志位)!再去新地址访问对象。

    这个过程被称为 “自愈”(Self-Healing)这次访问之后,这个引用本身就已经被修正了,下次再访问就是直接访问新对象,没有任何额外开销。 这是它与 Shenandoah 每次访问都可能需要检查的关键区别。

3. 优缺点
  • 优点停顿时间极短,同样与堆大小无关。“自愈”能力使得运行时开销远小于 Shenandoah,因此吞吐量表现通常比 Shenandoah 好得多,甚至接近 G1。无需像 G1 那样维护记忆集,节省了内存。

  • 缺点:实现极其复杂,严重依赖底层操作系统特性(如多重映射)。不支持分代收集(目前),可能导致浮动垃圾较多,抗突发流量能力稍弱。不过这是工程选择,并非技术不能实现。


总结与对比:你该选谁?

如何选择?

  • 追求极致低延迟,且运行在 OpenJDK 上:两者都是绝佳选择。

  • 同时非常关心吞吐量性能:优先尝试 ZGC,它的性能表现通常更均衡。

  • 需要使用 OracleJDK 并获得商业支持:只能选择 ZGC

  • 应用分配速率极高,堆内存巨大:目前两者都可能因为不分代而面临浮动垃圾的压力,需要预留足够堆内存。这是所有不分代收集器的共性问题。

总而言之,Shenandoah 和 ZGC 都代表了 JVM 垃圾收集技术的最高水平,它们通过不同的魔法将延迟降低到了前人无法想象的程度。ZGC 凭借其“魔法指针”和“自愈”能力,在实现上更显优雅,性能开销也更小,是目前更受瞩目的未来之星。

文章转载自:佛祖让我来巡山

原文链接:https://www.cnblogs.com/sun-10387834/p/19095137

体验地址:http://www.jnpfsoft.com/?from=001YH

http://www.dtcms.com/a/390055.html

相关文章:

  • 【测试】发版测试准入准出标准
  • 第一部分:HTML
  • 贪心算法应用:带权任务间隔调度问题详解
  • 视频监控大数据建模分析
  • IP的重要性
  • 远程访问管理爱快路由器
  • 算法 --- 优先级队列(堆)
  • Kindle出现电池感叹号图标和黄灯闪烁怎么办?
  • 摄像头模块在无人机上的应用
  • 深度学习篇GRU---LSTM和RNN的折中方案
  • Doris聚合表和物化视图选型对比
  • 互补色颜色对应的RGB
  • Python定义UDS诊断服务(8):SecurityAccess(0x27)
  • 第1节 工具(剪映剪映小助手)准备及安装(Coze扣子空间剪映小助手教程)
  • 大模型提示词Prompt工程:1-万能公式-完整指南
  • 8.30美团技术岗算法第三题
  • CentOS 7 一键安装 vsftpd 并创建可登录 FTP 用户 test
  • k8s自定义调度器实现路径
  • 服务器数据恢复—RAIDZ硬盘“惹祸”导致服务器崩溃的数据恢复过程
  • 20250917_车辆定位系统aidata-01Apache Doris数据库备份+恢复+清理 流程操作文档
  • Redis 7.0 ACL实战:RBAC模型实现精细化权限控制
  • Lightrag 文档处理不成功(httpx.ReadTimeout 为主)的解决步骤与方法总结
  • Spring Boot + MySQL MCP 集成标准流程
  • 基于RK3588与ZYNQ7045的ARM+FPGA+AI实时系统解决方案
  • 基于Linux,看清C++的动态库和静态库
  • 多导睡眠五大PSG数据集统一格式化处理|SHHS
  • ZeroMQ基础
  • 【JavaGuide学习笔记】什么,Java中 native 也是一个关键字?
  • 【LWIP】STM32F429 + LWIP + DP83848 热插拔问题总结
  • RGWRados::Object::Write::_do_write_meta()