游乐游手机版
首页/AI教程/文章详情

JVM垃圾回收ZGC/Shenandoah原理与JDK21分代优化

时间:2026-06-05 17:23
JVM垃圾回收:ZGC与Shenandoah 系统性知识体系(2026超高频) 一、整体定位与发展历程 1 1 低延迟垃圾收集器诞生背景 传统收集器(Serial、Parallel、CMS、G1)在堆内存突破100GB后,停顿时间直线上升——即便是G1,也常常在100ms以上徘徊。这显然不能满足现代

JVM垃圾回收:ZGC与Shenandoah 系统性知识体系(2026超高频)

一、整体定位与发展历程

1.1 低延迟垃圾收集器诞生背景

传统收集器(Serial、Parallel、CMS、G1)在堆内存突破100GB后,停顿时间直线上升——即便是G1,也常常在100ms以上徘徊。这显然不能满足现代云原生和实时系统的要求。于是,设计目标被重新定义:将GC停顿时间控制在亚毫秒级(<10ms),而且不能随堆大小膨胀而增加。实现的核心思路很纯粹:所有耗时操作都并发执行,只在万不得已时才短暂“Stop-The-World”。

1.2 发展历程对比

特性ZGCShenandoah
发起方OracleRed Hat
首次发布JDK 11(实验性)JDK 12(实验性)
正式转正JDK 15JDK 15
分代实现JDK 21(正式)JDK 17(实验性),JDK 21(正式)
最新版本JDK 23(持续优化)JDK 23(持续优化)
支持平台x86_64、AArch64、Windows、Linux、macOS全平台支持

二、ZGC核心原理与关键技术

2.1 核心设计思想

ZGC基于Region的内存布局:堆被划分成大小相等的Region(2MB/32MB/256MB,根据堆大小自动选择)。但它真正亮眼的技术“三剑客”是:着色指针(Colored Pointer)——利用64位指针的高几位来存储对象的元数据;读屏障(Load Barrier)——只在读取引用时执行一小段代码,用来处理并发转移;以及多映射内存(Multi-Mapping)——将同一块物理内存映射到多个虚拟地址空间。

2.2 着色指针技术详解

ZGC在64位指针的第42–45位塞进了4个标记位,分别承担不同角色:

  • Marked0(M0)——第42位,标记阶段0的存活对象
  • Marked1(M1)——第43位,标记阶段1的存活对象
  • Remapped(R)——第44位,表示对象已被转移到新地址
  • Finalizable(F)——第45位,表示对象有finalize方法需要执行

这么做的好处很明显:对象头里不再需要额外存储标记信息,内存省了;标记和转移可以完全并发进行;转移完成后不需要立即更新所有引用,通过读屏障自动修正即可。

2.3 读屏障技术

当应用线程读取一个对象引用时,ZGC会自动插入读屏障,伪代码逻辑大致如下:

Object load(Object* ref) {
    if (ref has Remapped bit set) {
        return ref;
    }
    // 尝试将引用更新到新地址
    Object* new_ref = get_forwarded_address(ref);
    // CAS更新引用
    if (CAS(ref, new_ref)) {
        return new_ref;
    }
    // 其他线程已经更新,重新读取
    return load(ref);
}

关键点:只有读操作才会触发屏障,写操作不需要。现代CPU对这种分支预测非常友好,开销极低——这也是ZGC能实现无停顿并发转移的基石。

2.4 ZGC垃圾回收周期

ZGC的回收周期分为四个主要阶段,其中只有两个极短暂的STW窗口:

  • 初始标记(STW,<1ms)——标记GC Roots直接引用的对象
  • 并发标记——遍历对象图,标记所有存活对象,和应用线程并发执行
  • 最终标记(STW,<1ms)——处理并发标记期间的引用变化
  • 并发转移——将存活对象复制到新的Region,同时通过读屏障修正引用
  • 并发重映射——修正那些没被读屏障处理到的引用(这个步骤可以与下一次标记合并)

三、Shenandoah核心原理与关键技术

3.1 核心设计思想

Shenandoah同样采用基于Region的内存布局,但它的核心技术是Brooks指针——每个对象头里额外保存一个指向自身的转发指针。配合读写屏障,在并发转移时进行重定向。此外,它还引入了一个连接矩阵(Connection Matrix)记录Region之间的引用关系,用来优化跨代引用处理。

3.2 Brooks指针技术

每个对象的头部结构比传统对象多了一个brooks_ptr字段:

struct ObjectHeader {
    mark_word;      // 标记字
    klass_ptr;      // 类指针
    brooks_ptr;     // 转发指针(Shenandoah特有)
};

运作原理:当对象被转移后,原对象的Brooks指针会指向新对象;所有对原对象的访问都会经过Brooks指针自动重定向到新地址。转移完成后,后台线程再逐步更新所有引用。简单说——用本体指向分身,分身搬家后本体指路。

3.3 Shenandoah垃圾回收周期

Shenandoah的回收周期比ZGC要复杂,总共包含9个阶段:

  • 初始标记(STW)——标记GC Roots直接引用
  • 并发标记——遍历对象图,标记存活对象
  • 最终标记(STW)——处理并发标记期间的引用变化
  • 并发清理——回收没有存活对象的Region
  • 并发准备转移——选择需要转移的Region集合
  • 初始转移(STW)——将GC Roots引用更新到新地址
  • 并发转移——将存活对象复制到新的Region
  • 最终更新引用(STW)——更新所有剩余的引用
  • 并发清理——回收转移完成后的旧Region

注意,它的STW阶段多达4个,但每个都控制在亚毫秒级别。

四、低延迟特性深度解析

4.1 低延迟的核心保障

  • 全并发执行:标记、转移、清理这些耗时的操作全部与应用线程并发跑,不阻塞业务。
  • 停顿时间与堆大小无关:STW只处理GC Roots和引用变化,堆再大也不会增加停顿。
  • 增量式处理:大任务被拆成多个小任务,避免一刀切的长暂停。
  • 预测性调度:根据应用负载动态调整GC线程的数量和执行时机。

4.2 典型停顿时间对比

收集器典型停顿时间最大停顿时间与堆大小关系
G1100-200ms500ms+随堆增大而增加
ZGC(不分代)<1ms<10ms几乎无关
Shenandoah<10ms<20ms几乎无关
ZGC(分代,JDK21+)<1ms<5ms几乎无关

4.3 吞吐量与延迟的权衡

ZGC和Shenandoah对比G1,吞吐量会低5%到15%,但延迟降低了一个数量级。两者之间:ZGC平均停顿时间更短,Shenandoah吞吐量略高一筹。决定选谁,关键是看你的场景是否对延迟极度敏感——比如微服务、实时系统、金融交易这些,就该优先考虑ZGC或Shenandoah。

五、JDK21分代ZGC优化(2026超高频考点)

5.1 分代ZGC诞生背景

不分代ZGC有个尴尬的地方:每次回收都要扫描整个堆,堆越大CPU开销越高;不能利用“弱分代假说”——短命对象反复被复制,低效;同时内存碎片问题依旧,时不时得做Full GC。分代设计正好补上这些短板:新生代对象存活率低,回收效率高;老年代对象存活率高,回收频率低。整体吞吐量上来了,CPU开销也降下去了。

5.2 分代ZGC内存布局

堆被划分为两个代:

  • 新生代:进一步分为Eden区和Survivor区,采用复制算法回收,频率高
  • 老年代:采用标记-整理算法回收,频率低,只在老年代满时才触发

5.3 分代ZGC关键优化点

  • 双标记位循环使用:新生代和老年代用不同的标记位(M0/M1),互不干扰,实现代独立回收。

  • 跨代引用处理:使用卡表(Card Table)记录老年代对新生代的引用。老年代被划分为512字节的卡,当老年代对象引用新生代对象时,对应卡被标记为脏卡。新生代回收时只需扫描脏卡,不用扫描整个老年代。卡表更新通过写屏障实现,开销极低。

  • 晋升机制优化:对象在Survivor区经历若干次回收后晋升到老年代;大对象直接分配在老年代;支持动态调整晋升阈值。

  • GC触发条件优化:新生代在Eden区满时触发,老年代在占用率达到阈值时触发,并且支持自适应调整。

5.4 分代ZGC性能提升

  • 吞吐量提升20%–50%(跟不分代相比)
  • CPU开销降低(新生代回收只扫描小部分堆)
  • 内存利用率提高,碎片减少
  • 停顿时间仍然保持在亚毫秒级,最大不超过5ms

六、ZGC vs Shenandoah 深度对比

6.1 核心技术对比

特性ZGCShenandoah
指针技术着色指针(利用指针高位)Brooks指针(对象头中)
屏障类型仅读屏障读写屏障
内存布局基于Region基于Region
转移方式并发转移 + 读屏障修正并发转移 + Brooks指针重定向
分代实现JDK21正式支持JDK21正式支持

6.2 性能对比

指标ZGCShenandoahG1
平均停顿时间<1ms<10ms100-200ms
最大停顿时间<5ms(分代)<20ms500ms+
吞吐量中等(分代后大幅提升)中等偏高
内存开销低(着色指针)中等(Brooks指针)
CPU开销中等中等偏高

6.3 适用场景

ZGC优先:对延迟要求极高(<10ms)、大堆(100GB+)、微服务/实时系统/金融交易、JDK21及以上。

Shenandoah优先:对吞吐量有一定要求的低延迟应用、需要全平台支持、JDK17及以上。

G1优先:对吞吐量要求高、延迟要求一般(100-200ms)、堆大小较小(<32GB)、或JDK8及以下。

七、调优指南与最佳实践

7.1 ZGC调优参数

# 启用ZGC
-XX:+UseZGC
# 启用分代ZGC(JDK21+,默认开启)
-XX:+ZGenerational
# 设置堆大小
-Xms16g -Xmx16g
# 设置GC线程数(默认等于CPU核心数)
-XX:ConcGCThreads=4
# 设置最大停顿时间目标(默认200ms,ZGC会尽力满足)
-XX:MaxGCPauseMillis=5
# 启用大页支持(提升性能)
-XX:+UseLargePages

7.2 Shenandoah调优参数

# 启用Shenandoah
-XX:+UseShenandoahGC
# 启用分代Shenandoah(JDK21+)
-XX:+ShenandoahGenerational
# 设置GC线程数
-XX:ConcGCThreads=4
# 设置最大停顿时间目标
-XX:MaxGCPauseMillis=10

7.3 最佳实践

  • 堆大小:建议设为物理内存的50%–70%,避免过小导致频繁GC。
  • GC线程数:默认值通常够用,别设置太多,以免跟应用线程抢CPU。
  • 大页支持:强烈建议开启,能显著提升性能。
  • JDK版本:尽可能用最新的JDK(JDK21+),分代ZGC带来的收益非常可观。
  • 监控指标:重点关注GC停顿时间、GC频率、吞吐量和CPU使用率。

八、2026面试高频考点

8.1 基础概念题

  • ZGC和Shenandoah的核心设计思想是什么?
  • 着色指针和Brooks指针的区别是什么?
  • 读屏障和写屏障在ZGC和Shenandoah中的作用是什么?
  • 为什么ZGC的停顿时间与堆大小无关?

8.2 原理深度题

  • 详细描述ZGC的垃圾回收周期,指出哪些阶段是STW的?
  • 分代ZGC解决了不分代ZGC的哪些问题?
  • 分代ZGC的内存布局是怎样的?跨代引用如何处理?
  • Shenandoah的连接矩阵有什么作用?

8.3 对比分析题

  • ZGC vs Shenandoah vs G1的优缺点对比?
  • 分代ZGC相比不分代ZGC有哪些性能提升?
  • 什么场景下应该选择ZGC,什么场景下应该选择Shenandoah?

8.4 调优实践题

  • 如何开启分代ZGC?有哪些关键调优参数?
  • 如果ZGC的停顿时间过长,应该如何排查和调优?
  • 大堆场景下使用ZGC有哪些注意事项?

九、未来发展趋势

  • 进一步降低停顿:目标是亚微秒级。
  • 自动调优增强:根据应用负载自动调整GC参数。
  • 硬件加速:利用A VX-512等现代CPU特性加速GC操作。
  • 统一垃圾回收框架:JDK正在逐步统一不同收集器的代码框架。
  • 弹性堆大小:支持根据应用负载动态调整堆大小。

ZGC与Shenandoah 面试背诵问答卡片(2026超高频)

一、基础概念题(必背)

问题:ZGC和Shenandoah诞生的核心背景与设计目标是什么?
答案:传统收集器(如G1)在100GB+大堆下停顿时间过长(>100ms)且随堆增大而增加。核心设计目标是将GC停顿控制在亚毫秒级(<10ms),且停顿时间与堆大小无关,通过并发执行所有耗时操作实现。

问题:ZGC的三大核心技术是什么?
答案:① 基于Region的内存布局(2MB/32MB/256MB自动选择);② 着色指针(利用64位指针高位存储对象元数据);③ 读屏障(仅在读操作时触发,实现并发转移)。

问题:Shenandoah的三大核心技术是什么?
答案:① 基于Region的内存布局;② Brooks指针(对象头中存储转发指针);③ 读写屏障(读写操作均触发,配合Brooks指针实现重定向)。

问题:为什么ZGC和Shenandoah的停顿时间与堆大小无关?
答案:它们的STW阶段仅处理GC Roots和引用变化,不扫描整个堆。所有耗时的标记、转移、清理操作均与应用线程并发执行,停顿时间只与GC Roots数量和引用变化率有关,与堆总大小无关。

二、核心原理题(高频)

问题:详细解释ZGC的着色指针技术,四个标记位分别是什么?
答案:ZGC利用64位指针的第42–45位存储4个标记位:M0(第42位)标记阶段0的存活对象;M1(第43位)标记阶段1的存活对象;Remapped(第44位)表示对象已被转移到新地址;Finalizable(第45位)表示对象有finalize方法需要执行。优势:无需对象头存储标记位,标记和转移可完全并发,转移后无需立即更新所有引用。

问题:ZGC的读屏障是如何工作的?
答案:当应用线程读取对象引用时,自动插入读屏障:检查引用的Remapped位是否已设置,是则直接返回;否则获取对象的转发地址,通过CAS原子更新引用到新地址,最后返回新地址的对象。特点:仅读操作触发,开销极低,实现了无停顿并发转移。

问题:ZGC的垃圾回收周期包含哪些阶段?哪些是STW的?
答案:共5个阶段,仅2个极短STW阶段:初始标记(STW,<1ms)标记GC Roots直接引用;并发标记遍历对象图标记存活对象;最终标记(STW,<1ms)处理并发标记期间的引用变化;并发转移将存活对象复制到新Region;并发重映射修正剩余未更新的引用(可与下一次标记合并)。

问题:Shenandoah的Brooks指针是如何工作的?
答案:每个对象头额外存储一个转发指针,初始指向自身。当对象被转移时,原对象的Brooks指针指向新对象。所有对原对象的访问都会通过Brooks指针自动重定向到新对象,转移完成后后台线程逐步更新所有引用。

问题:Shenandoah的垃圾回收周期包含哪些关键STW阶段?
答案:共4个STW阶段,均为亚毫秒级:初始标记(标记GC Roots直接引用)、最终标记(处理并发标记的引用变化)、初始转移(更新GC Roots引用到新地址)、最终更新引用(更新所有剩余引用)。

三、JDK21分代ZGC专题(2026超高频)

问题:不分代ZGC存在哪些核心痛点?分代ZGC解决了什么问题?
答案:不分代ZGC痛点:每次回收扫描整个堆,大堆CPU开销高;无法利用弱分代假说,大量短生命周期对象被重复复制;内存碎片问题仍存在,需定期Full GC。分代ZGC解决:吞吐量提升20%–50%,CPU开销降低,内存利用率提高,同时保持亚毫秒级停顿。

问题:分代ZGC的内存布局是怎样的?
答案:将堆划分为两个独立代:新生代(Eden+Survivor)采用复制算法,回收频率高;老年代采用标记-整理算法,回收频率低。两代使用不同的标记位(M0/M1)循环,实现代独立回收。

问题:分代ZGC如何处理跨代引用?
答案:使用卡表(Card Table)记录老年代对新生代的引用。老年代被划分为512字节的卡,当老年代对象引用新生代对象时,对应卡被标记为脏卡。新生代回收时只需扫描脏卡,无需扫描整个老年代,卡表更新通过写屏障实现。

问题:分代ZGC相比不分代ZGC有哪些关键性能提升?
答案:吞吐量提升20%–50%;CPU使用率降低约30%;内存碎片显著减少,内存利用率提高;最大停顿时间从<10ms进一步降低到<5ms;大幅降低了大堆场景下的Full GC概率。

问题:JDK21中分代ZGC是默认开启的吗?如何手动开启/关闭?
答案:JDK21及以上版本中,分代ZGC默认开启。手动开启:-XX:+ZGenerational;手动关闭(使用不分代ZGC):-XX:-ZGenerational

四、对比分析题(高频)

问题:ZGC和Shenandoah的核心技术区别是什么?
答案:

维度ZGCShenandoah
指针技术着色指针(利用指针高位)Brooks指针(对象头中)
屏障类型仅读屏障读写屏障
内存开销低(无额外对象头开销)中等(每个对象多一个指针)
平均停顿<1ms<10ms
吞吐量中等(分代后大幅提升)中等偏高

问题:ZGC、Shenandoah和G1的适用场景分别是什么?
答案:ZGC适合对延迟要求极高(<10ms)的大堆(100GB+)场景,如金融交易、实时系统、微服务,推荐JDK21+;Shenandoah适合对吞吐量有一定要求的低延迟应用,需要全平台支持,推荐JDK21+;G1适合对吞吐量要求高、延迟要求一般(100-200ms)的中小堆(<32GB)场景,或JDK8及以下。

问题:分代ZGC和分代Shenandoah哪个更推荐使用?
答案:JDK21及以上版本优先推荐分代ZGC。它的平均停顿时间更短(<1ms vs <10ms),内存开销更低,且经过Oracle更充分的优化和测试。分代Shenandoah适合需要全平台支持或对Red Hat生态有依赖的场景。

五、调优实践题(高频)

问题:开启ZGC的核心JVM参数有哪些?
答案:

# 启用ZGC(JDK15+)
-XX:+UseZGC
# 启用分代ZGC(JDK21+,默认开启)
-XX:+ZGenerational
# 设置堆大小(建议物理内存的50%-70%)
-Xms16g -Xmx16g
# 设置GC线程数(默认等于CPU核心数)
-XX:ConcGCThreads=4
# 设置最大停顿时间目标(默认200ms,ZGC会尽力满足)
-XX:MaxGCPauseMillis=5
# 启用大页支持(强烈推荐,性能提升显著)
-XX:+UseLargePages

问题:如果ZGC的停顿时间过长,应该如何排查和调优?
答案:检查GC日志,确认是哪个STW阶段耗时过长。若初始标记/最终标记过长,减少GC Roots数量(如减少线程数);若并发阶段跟不上应用速度,增加ConcGCThreads;适当调大堆大小减少GC频率;启用大页支持降低内存访问延迟;升级到最新JDK版本(JDK21+分代ZGC停顿更短)。

问题:大堆场景下使用ZGC有哪些最佳实践?
答案:必须使用JDK21及以上版本的分代ZGC;堆大小建议设为物理内存的50%–70%;启用大页支持(透明大页或巨页);GC线程数设为CPU核心数的1/4–1/2,避免与应用线程竞争;不要设置过小的MaxGCPauseMillis,否则会导致GC频率过高;重点监控GC频率、停顿时间和CPU使用率。

来源:https://developer.aliyun.com/article/1737779
上一篇中小型项目OpenClaw适配布设方案及完整实施步骤 下一篇OpenClaw长篇内容优化调节方法
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Synthesia零基础教程:客户端安装与工作区权限设置
AI教程 · 2026-06-07

Synthesia零基础教程:客户端安装与工作区权限设置

本文介绍了AI视频生成工具Synthesia的入门流程。内容涵盖从官网下载客户端、完成账户注册与登录,到软件安装与启动的完整步骤。详细说明了如何初始化工作区,包括创建首个AI视频项目、选择模板与AI主播。最后,指导用户理解并设置团队协作中的不同权限角色,以便安全高效地共同管理项目。

FramePack新手入门指南:安装启动报错修复导出全流程
AI教程 · 2026-06-07

FramePack新手入门指南:安装启动报错修复导出全流程

本文详细介绍了FramePack工具从下载安装到项目导出的完整流程。内容涵盖软件安装步骤、首次启动设置、常见报错解决方案以及项目打包导出方法。指南旨在帮助用户快速掌握工具核心操作,解决使用过程中可能遇到的技术问题,确保顺利完成AI视频帧处理任务。

FLUX.1保姆级教程:环境安装、显存优化与首次出图测试
AI教程 · 2026-06-07

FLUX.1保姆级教程:环境安装、显存优化与首次出图测试

本文详细介绍了FLUX 1的安装与初步使用流程。内容涵盖从Python环境配置、代码仓库克隆、依赖包安装,到关键的显存优化设置,最后指导用户完成首次文生图测试。教程旨在帮助用户顺利搭建运行环境,解决常见安装问题,并实现基础图像生成功能。

AnythingLLM新手实战:本地大模型部署后知识库接入设置
AI教程 · 2026-06-07

AnythingLLM新手实战:本地大模型部署后知识库接入设置

本文介绍了在本地部署大模型后,如何为AnythingLLM设置知识库。内容涵盖知识库的基本概念、创建与配置步骤、文档上传与处理技巧,以及如何通过问答测试其效果。旨在帮助用户有效整合本地文档资源,构建个性化的AI知识助手,提升信息检索与利用效率。

Aider安装失败排查:扩展冲突与登录异常全解析
AI教程 · 2026-06-07

Aider安装失败排查:扩展冲突与登录异常全解析

本文针对Aider安装过程中常见的扩展冲突与登录异常问题,提供了系统的排查思路与解决方案。内容涵盖如何识别并处理与其他AI工具的兼容性问题,解决因网络或账户设置导致的登录失败,以及通过环境检查、依赖更新等步骤彻底排除安装障碍,帮助用户顺利完成安装与配置。