🍂 枫言枫语 :我是予枫,一名行走在 Java 后端与多模态 AI 交叉路口的研二学生。
"予一人以深耕,观万木之成枫。"
在这里,我记录从底层源码到算法前沿的每一次思考。希望能与你一起,在逻辑的丛林中寻找技术的微光。

在Java应用性能优化中,垃圾回收器(GC)的选型直接决定系统的吞吐量、延迟与资源利用率,尤其在JDK 8向17/21迁移的场景下,GC的适配与调优更是核心环节。JDK生态中,G1、ZGC、Shenandoah三款回收器凭借各自的设计优势,覆盖了从常规业务到高并发低延迟的全场景需求------G1作为成熟稳定的"全能型"回收器,ZGC以亚毫秒级停顿突破大堆瓶颈,Shenandoah则主打低延迟与跨版本兼容。本文将从工作原理、核心特性、性能对比、实战配置及选型逻辑五大维度,全面解析三款回收器,助力开发者结合业务场景精准选型。
注:本文基于JDK 17(LTS)与JDK 21(LTS)版本展开,两款版本中ZGC已正式稳定,Shenandoah为实验性/正式特性(视版本而定),均适配前文提及的JDK迁移场景,文中会明确标注各回收器的版本支持范围。
一、三款垃圾回收器核心定位与版本适配
在深入原理前,先明确三款回收器的设计目标、JDK版本支持及核心适用场景,为选型建立基础认知。三者的定位差异直接决定了其在不同业务中的适配性,尤其需结合迁移后的JDK版本特性选择。
| 回收器类型 | 核心设计目标 | JDK版本支持 | 堆内存适配范围 | 核心适用场景 |
|---|---|---|---|---|
| G1(Garbage-First) | 兼顾吞吐量与延迟,平衡资源占用 | JDK 7u4+引入,JDK 11起为默认GC,JDK 17/21持续优化 | 几GB至数十GB(推荐≤64GB) | 常规微服务、单体应用、电商中台等对延迟有一定要求但非极致场景 |
| ZGC(Z Garbage Collector) | 低延迟(亚毫秒级停顿),支持大堆 | JDK 11实验性,JDK 17正式稳定,JDK 21性能增强 | 数十GB至TB级(推荐≥16GB) | 金融交易、实时计算、高并发API等极致低延迟、大内存场景 |
| Shenandoah | 低延迟,与应用线程并发执行回收,跨版本兼容 | JDK 12实验性,JDK 17仍为实验性(OpenJDK),JDK 21正式稳定 | 几GB至TB级 | 对延迟敏感但无法使用ZGC(如依赖特定JDK发行版)、混合堆大小场景 |
关键提示:JDK 17中移除了CMS回收器,若从JDK 8迁移后仍依赖CMS的低延迟特性,需优先切换至G1、ZGC或Shenandoah;JDK 21中ZGC与Shenandoah均已优化成熟,可根据业务需求任选。
二、核心原理拆解:三款回收器的底层逻辑差异
三款回收器的性能差异源于底层设计逻辑的不同------G1采用"分区+标记-整理"的混合模式,ZGC与Shenandoah则通过"并发标记-整理"突破停顿瓶颈,但两者的并发实现机制存在本质区别。
1. G1:分区式混合回收,平衡吞吐与延迟
G1是第一款面向服务端应用的垃圾回收器,核心设计是"将堆内存分区管理,优先回收垃圾最多的区域",既保留了CMS的并发标记优势,又解决了CMS内存碎片的痛点。
1.1 核心架构
G1将堆内存划分为多个大小相等的独立区域(Region),每个Region大小可通过`-XX:G1HeapRegionSize`参数配置(1MB~32MB,需为2的幂),Region分为三种类型:
-
Eden Region:存放新创建的对象,当Eden区域占满时触发Minor GC,采用复制算法将存活对象转移至Survivor Region。
-
Survivor Region:存放Minor GC后存活的对象,多次存活后(达到年龄阈值)转移至Old Region。
-
Old Region:存放老年代对象,通过Major GC(Full GC)回收,采用标记-整理算法消除内存碎片。
此外,G1还设有Humongous Region,专门存放超过Region大小50%的大对象,直接分配至老年代,避免大对象频繁迁移导致的性能损耗。
1.2 工作流程
G1的回收流程分为四个核心阶段,兼顾并发与串行操作,通过动态调整回收区域实现延迟控制:
-
初始标记(Initial Mark):暂停所有应用线程(STW),标记GC Roots直接关联的对象,耗时极短(毫秒级)。
-
并发标记(Concurrent Mark):恢复应用线程,并发遍历对象引用链,标记所有存活对象,此阶段不影响应用运行,耗时较长但可与业务线程并行。
-
最终标记(Final Mark):再次暂停应用线程(STW),处理并发标记阶段遗漏的对象(如新增引用、消失引用),耗时较短。
-
筛选回收(Live Data Counting and Evacuation):暂停应用线程(STW),统计各Region的垃圾占比,优先回收垃圾最多的Region("Garbage-First"命名由来),采用复制算法将存活对象转移至空Region,同时整理内存碎片。
核心优势:通过`-XX:MaxGCPauseMillis`参数可指定最大GC停顿时间(默认200ms),G1会动态调整回收的Region数量,确保停顿时间不超过阈值;标记-整理算法消除内存碎片,无需频繁Full GC。
2. ZGC:基于着色指针,突破大堆低延迟瓶颈
ZGC是Oracle JDK推出的革命性低延迟回收器,核心设计基于"着色指针"与"读屏障"技术,实现了几乎全程并发的垃圾回收,将STW停顿控制在亚毫秒级,同时支持TB级堆内存。
2.1 核心技术:着色指针与读屏障
这是ZGC与传统回收器的核心区别,也是其低延迟的关键:
-
着色指针(Colored Pointers):ZGC将对象指针的高几位(空闲位)用作标记位,存储对象的状态信息(如是否被标记、是否需要迁移),无需额外维护标记位图,减少内存占用与操作开销。指针着色不影响应用逻辑,由JVM层面透明处理。
-
读屏障(Load Barrier):当应用线程读取对象指针时,触发读屏障检查指针状态,若指针标记为"需迁移",则先将对象迁移至新地址,再更新指针指向新地址,确保应用线程始终访问有效对象。读屏障开销极低,对应用性能影响可忽略。
2.2 工作流程
ZGC的回收流程全程几乎并发,仅初始标记与最终标记存在极短STW,核心分为五个阶段:
-
初始标记(Initial Mark):STW,标记GC Roots直接关联的对象,耗时通常在1ms以内。
-
并发标记(Concurrent Mark):并发遍历对象引用链,通过着色指针标记存活对象,与应用线程并行,无停顿影响。
-
并发预备重定位(Concurrent Prepare for Relocation):筛选需要迁移的Region(如垃圾占比高、内存碎片严重的区域),为迁移做准备,并发执行。
-
并发重定位(Concurrent Relocate):通过读屏障触发对象迁移,应用线程访问需迁移对象时,自动协助完成迁移,JVM后台线程同时批量迁移未被访问的对象,此阶段无STW,完全并发。
-
并发清理(Concurrent Cleanup):回收已完成迁移的旧Region,释放内存空间,并发执行,不影响应用运行。
核心优势:STW停顿极短(通常≤1ms),不受堆大小影响(大堆场景下优势更明显);支持动态扩缩容,内存碎片少;对应用性能影响极低,适合高并发低延迟场景。
3. Shenandoah:并发整理,跨发行版兼容的低延迟方案
Shenandoah由Red Hat主导开发,目标与ZGC一致(低延迟、大堆支持),但采用不同的技术路径------不依赖着色指针(适配更多CPU架构),而是通过"并发标记-并发整理"实现低延迟,且兼容更多JDK发行版(如OpenJDK、Adoptium Temurin)。
3.1 核心技术:连接矩阵与并发整理
Shenandoah的核心创新的是"连接矩阵"与"并发整理"机制,替代着色指针实现对象迁移与引用更新:
-
连接矩阵(Connection Matrix):维护对象间的引用关系,在并发标记阶段记录引用变化,避免遗漏标记;在整理阶段通过矩阵快速定位需要更新的引用,提升效率。
-
并发整理(Concurrent Compaction):与ZGC的"按需迁移"不同,Shenandoah采用批量迁移模式,后台线程并发将存活对象迁移至新区域,同时通过写屏障更新引用,全程无需长时间STW。
3.2 工作流程
Shenandoah的回收流程与ZGC类似,核心差异在于整理阶段的实现,全程STW停顿极短:
-
初始标记(Initial Mark):STW,标记GC Roots直接关联的对象,耗时极短。
-
并发标记(Concurrent Mark):并发遍历对象引用链,标记存活对象,通过连接矩阵记录引用变化,与应用线程并行。
-
最终标记(Final Mark):STW,处理标记阶段遗漏的引用,耗时较短。
-
并发整理(Concurrent Compact):后台线程并发迁移存活对象,整理内存碎片,同时通过写屏障更新应用线程的对象引用,无STW停顿。
-
并发清理(Concurrent Cleanup):回收空Region,释放内存,并发执行。
核心优势:不依赖特定CPU架构(着色指针需64位CPU支持),兼容性更强;并发整理效率高,延迟表现接近ZGC;适合无法使用ZGC(如依赖非Oracle JDK)的低延迟场景。
三、性能指标全对比:吞吐量、延迟与资源开销
选型的核心是匹配业务的性能需求,需从吞吐量、延迟、内存开销、CPU占用四个关键指标,对比三款回收器的表现,结合实际场景取舍。以下基于JDK 17环境、64GB堆内存、IO密集型应用(微服务)的测试数据(仅供参考):
1. 核心指标对比
| 指标类型 | G1 | ZGC | Shenandoah |
|---|---|---|---|
| 单次GC STW停顿 | 10~100ms(取决于回收区域数量) | ≤1ms(亚毫秒级,堆越大优势越明显) | 1~5ms(略高于ZGC,低于G1) |
| 吞吐量 | 较高(90%~95%),停顿越长吞吐量越高 | 中等(85%~90%),并发开销略高 | 中等(85%~90%),与ZGC接近 |
| 内存开销 | 较低(堆内存利用率≥70%) | 较高(需预留20%~30%内存用于迁移) | 中等(需预留15%~25%内存用于整理) |
| CPU占用 | 较低(并发阶段CPU消耗少) | 较高(并发标记、迁移需占用更多CPU) | 较高(与ZGC接近,略低于ZGC) |
| 内存碎片 | 低(标记-整理算法) | 极低(并发迁移+整理) | 极低(并发整理算法) |
2. 关键结论
-
延迟敏感场景(如金融交易、实时接口):ZGC>Shenandoah>G1,ZGC的亚毫秒级停顿优势显著,Shenandoah作为替代方案适配更多环境。
-
吞吐量优先场景(如报表计算、批处理任务):G1>ZGC≈Shenandoah,G1通过调整停顿时间可兼顾吞吐量与延迟。
-
大堆场景(≥32GB):ZGC>Shenandoah>G1,G1在大堆下停顿时间会显著增加,而ZGC与Shenandoah的停顿不受堆大小影响。
-
资源受限场景(CPU/内存不足):G1更优,ZGC与Shenandoah的并发操作需占用更多CPU与内存资源。
四、实战配置指南:分场景参数优化
基于JDK 17/21环境,针对不同业务场景提供三款回收器的核心配置参数,结合前文JDK迁移场景,确保配置与高版本JDK兼容,同时规避常见调优误区。
1. G1实战配置(常规微服务、单体应用)
核心目标:平衡吞吐量与延迟,适配几GB至64GB堆内存,适合大多数迁移后的常规业务。
# 启用G1回收器(JDK 11+默认,可显式指定)
-XX:+UseG1GC
# 设置最大GC停顿时间(根据业务调整,推荐50~200ms)
-XX:MaxGCPauseMillis=100
# 设置堆内存大小(根据服务器配置调整,建议-Xms与-Xmx一致)
-Xms16G -Xmx16G
# 设置Region大小(默认自动计算,建议手动指定为2~8MB,大堆可设为16MB)
-XX:G1HeapRegionSize=4M
# 设置老年代占用堆内存的阈值(默认45%,超过则触发混合回收)
-XX:InitiatingHeapOccupancyPercent=40
# 关闭自适应大小调整(手动调优时建议关闭,保持参数稳定)
-XX:-UseAdaptiveSizePolicy
# GC日志配置(JDK 17+统一格式)
-Xlog:gc*:file=g1-gc.log:time,level,tags:filecount=10,filesize=100m
调优误区:过度追求低停顿(如将MaxGCPauseMillis设为20ms以下),会导致G1频繁触发小范围回收,反而降低吞吐量;Region大小过大易导致内存碎片,过小则增加管理开销。
2. ZGC实战配置(高并发低延迟、大堆场景)
核心目标:极致低延迟,适配16GB以上大堆,适合金融交易、实时计算等场景(JDK 17+启用)。
# 启用ZGC回收器
-XX:+UseZGC
# 设置堆内存大小(大堆场景推荐32GB以上,-Xms与-Xmx一致)
-Xms64G -Xmx64G
# 设置ZGC区域大小(默认自动计算,大堆建议设为8~32MB)
-XX:ZHeapRegionSize=8M
# 启用并发线程数自动调整(根据CPU核心数适配,默认1/8 CPU核心)
-XX:+ZGCConcurrentThreadsAuto
# 启用动态堆扩缩容(根据内存使用情况自动调整堆大小,适合弹性场景)
-XX:+ZGCHeapExpansion
-XX:+ZGCHeapShrink
# GC日志配置(记录详细并发阶段信息)
-Xlog:gc*:file=zgc-gc.log:time,level,tags:filecount=10,filesize=100m
调优误区:ZGC无需复杂调优,过度调整并发线程数反而可能导致CPU竞争;堆内存不足(预留空间<20%)会影响迁移效率,导致停顿增加。
3. Shenandoah实战配置(低延迟、跨发行版场景)
核心目标:低延迟,适配无法使用ZGC的环境(如OpenJDK),JDK 17需显式启用实验性特性,JDK 21可直接使用。
# 启用Shenandoah回收器(JDK 17需加--enable-preview,JDK 21无需)
-XX:+UseShenandoahGC --enable-preview
# 设置堆内存大小
-Xms32G -Xmx32G
# 设置并发整理线程数(建议为CPU核心数的1/4~1/2)
-XX:ShenandoahGCThreads=8
# 设置停顿时间目标(推荐1~5ms)
-XX:ShenandoahPauseTarget=3
# 启用快速模式(减少STW停顿,适合低延迟场景)
-XX:+ShenandoahQuickStripMine
# GC日志配置
-Xlog:gc*:file:shenandoah-gc.log:time,level,tags:filecount=10,filesize=100m
调优误区:JDK 17中Shenandoah为实验性特性,不建议用于核心生产环境;CPU核心数不足时,过多的并发线程会导致性能退化。
五、选型实战逻辑:结合业务场景精准决策
选型并非追求"最优",而是"最适配",需结合业务类型、堆内存大小、服务器资源、延迟要求四大核心因素,结合JDK迁移后的版本特性决策,以下为具体选型路径:
1. 第一步:明确核心业务诉求
-
若为常规业务(如管理后台、电商中台、普通微服务),对延迟要求适中(允许100ms以内停顿),追求稳定性与吞吐量,优先选择G1------成熟稳定,调优成本低,适配大多数迁移场景。
-
若为核心业务(如金融支付、实时接口、高频交易),对延迟要求极致(允许≤5ms停顿),且堆内存较大(≥16GB),优先选择ZGC(JDK 17+)------亚毫秒级停顿,大堆场景优势显著。
-
若为低延迟业务,但依赖OpenJDK等非Oracle发行版,或CPU架构不支持着色指针,选择Shenandoah(JDK 21+推荐)------延迟表现接近ZGC,兼容性更强。
2. 第二步:结合资源与版本适配
-
服务器资源受限(CPU核心数≤8、内存≤16GB):优先G1,ZGC与Shenandoah的并发开销会占用大量资源,导致业务性能下降。
-
JDK版本为17:G1为默认选择,ZGC已稳定可用于生产,Shenandoah为实验性(谨慎使用)。
-
JDK版本为21:ZGC与Shenandoah均已成熟,低延迟场景可任选,常规场景仍推荐G1。
3. 第三步:试点验证与优化
选型后需在测试环境进行全量验证,避免直接上线导致风险:
-
性能压测:模拟生产流量,对比迁移前后的吞吐量、响应时间、GC停顿次数/时长,确保性能不退化。
-
稳定性测试:长时间运行(24~72小时),监控GC日志、内存占用、CPU使用率,排查内存泄漏、频繁GC等问题。
-
灰度部署:核心业务建议灰度上线,先部署部分节点验证,无问题后全量发布,同时预留回滚方案。
六、总结
G1、ZGC、Shenandoah三款回收器分别对应不同的业务场景,其核心差异源于底层设计逻辑的取舍------G1平衡吞吐与延迟,是迁移后的"默认安全选择";ZGC以着色指针突破低延迟瓶颈,是大堆场景的"极致方案";Shenandoah以并发整理实现跨环境兼容,是低延迟场景的"备选方案"。
在JDK 8→17/21的迁移过程中,GC选型需结合版本特性与业务需求:常规场景无需刻意更换,沿用G1并优化参数即可;低延迟、大堆场景可升级至ZGC或Shenandoah,充分发挥高版本JDK的性能优势。同时,调优的核心是"按需配置",避免过度调优,通过测试验证确保配置适配业务,才能实现系统性能与稳定性的最大化。
未来,随着JDK版本的迭代,ZGC与Shenandoah的性能与兼容性将进一步优化,低延迟回收器将逐步成为大堆场景的主流,而G1仍将在常规场景中占据重要地位,开发者需持续关注版本特性,结合业务演进调整选型策略。
关于作者 : 💡 予枫 ,某高校在读研究生,专注于 Java 后端开发与多模态情感计算。💬 欢迎点赞、收藏、评论,你的反馈是我持续输出的最大动力!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=9wrxwtlju1l
当前加入还有惊喜相送!