Shenandoah:Red Hat 的低延迟垃圾收集器#
版本信息
Shenandoah 与 ZGC 同属并发整理型低延迟 GC——目标相似(把回收与应用线程并发、压低停顿),但技术路线不同:它用转发指针而非 ZGC 的染色指针。本页讲清它的原理、与 ZGC 的差异,以及最关键的「我的 JDK 有没有它」。
设计目标:并发整理、低停顿#
Shenandoah(Red Hat 主导)的设计目标与 ZGC 一致:把绝大部分回收(含对象整理/转移)与应用线程并发,使停顿与堆大小基本无关。它的核心区别在于实现「并发转移」的技术:
- 转发指针(forwarding pointer):并发转移时,GC 把对象复制到新位置,在旧位置编码一个指向新副本的转发指针。早期(JDK 12 实验)用独立的 Brooks 指针字(每对象多一个字);JDK 13 起复用对象头 mark word 存储转发信息,不再额外占字。
- 读屏障:应用线程访问对象时,读屏障跟随转发指针到达对象的当前位置——无论对象是否正在被转移,都能拿到正确副本。
这让「转移对象」与应用线程并发完成,从而避免 STW 整堆整理。
工作原理#
graph LR
App["应用线程运行"] -.并发.- C["并发标记 / 转移<br/>转发指针 + 读屏障"]
App --> P["极短 STW<br/>初始标记 / 最终标记等"]
C --> D["转发指针 → 新副本<br/>应用经转发指针访问"]
style App fill:#c8e6c9
style P fill:#ffe0b2
style C fill:#bbdefb
Shenandoah 的回收周期也是「初始标记(STW)→ 并发标记 → 最终标记(STW)→ 并发转移/整理」的形态,关键是转移并发——借助转发指针,应用线程在转移进行中仍能访问对象,无需等待。
Shenandoah vs ZGC#
两者目标相同,技术细节有别:
| 维度 | ZGC | Shenandoah |
|---|---|---|
| 并发转移机制 | 染色指针(指针位编码状态) + 读屏障 | 转发指针(复用 mark word) + 读屏障 |
| 内存开销 | 染色位编码,较省 | 转发元数据复用 mark word,开销低 |
| 停顿 | <1ms,与堆无关 | 亚毫秒,与堆基本无关 |
| 分代 | JDK 21 起(JEP 439),JDK 23 默认 | JDK 24 实验(JEP 404)、JDK 25 产品(JEP 521) |
| 主导 / 发行版 | Oracle;多数 OpenJDK 构建可用 | Red Hat;仅部分发行版可用 |
选哪个往往不取决于性能微差,而取决于「你的 JDK 发行版里有没有」——多数 OpenJDK 构建带 ZGC,Shenandoah 则集中在 Red Hat / Corretto 等。
JVM 配置参数#
# 仅当发行版包含 Shenandoah 时有效
java -XX:+UseShenandoahGC -Xmx16g -jar app.jar
| 参数 | 含义 | 备注 |
|---|---|---|
-XX:+UseShenandoahGC |
启用 Shenandoah | 发行版不含时报 Unrecognized VM option |
-XX:ShenandoahGCHeuristics=<mode> |
回收触发启发式策略 | adaptive(默认)/ static / passive / aggressive / compact(取值集合随 JDK 版本略有增减) |
-XX:ConcGCThreads=<n> |
并发线程数 | 占用应用 CPU |
-XX:ParallelGCThreads=<n> |
STW 点的并行线程数 |
# 典型:Red Hat 系发行版上的大堆低延迟服务
java -XX:+UseShenandoahGC -Xmx32g \
-XX:ShenandoahGCHeuristics=adaptive \
-jar service.jar
适用场景#
- 大堆 + 低延迟 + Red Hat 系发行版:如果你的 JDK 是 Red Hat/Fedora/Corretto 等,Shenandoah 是与 ZGC 并列的低延迟选择。
- 与 ZGC 二选一:性能目标相近,按发行版可用性和团队偏好定。
与前代 GC 对比:G1 / ZGC → Shenandoah#
Shenandoah 与 ZGC 同属并发整理型低延迟 GC,目标都是亚毫秒停顿。相对现代默认 G1(部分阶段仍 STW、大堆停顿上升),Shenandoah 把整理也并发化;相对 ZGC,它用转发指针(而非染色指针)实现并发转移——技术路线不同,目标相近。
java -XX:+UseG1GC -Xmx32g -jar app.jar # 停顿可控,但整理阶段仍有 STW
java -XX:+UseZGC -Xmx32g -jar app.jar # 亚毫秒,染色指针 + 读屏障
java -XX:+UseShenandoahGC -Xmx32g -jar app.jar # 亚毫秒,转发指针 + 读屏障
常见坑 / 最佳实践#
- 先确认发行版有没有它:Oracle JDK 与不少 OpenJDK 构建不含 Shenandoah,
-XX:+UseShenandoahGC直接Unrecognized VM option。选型前先在目标 JDK 上验证。 - 转发指针的实现演进:早期(JDK 12)用独立 Brooks 指针字、内存开销明显;JDK 13 起复用 mark word,额外开销近乎消除。现代版本不必担心转发指针占内存。
- 分代看版本:JDK 25 起 Generational Shenandoah 转正(JEP 521);早期版本是非分代的,开销相对高。
- 不要假设它与 ZGC 完全等价:尽管目标相似,停顿曲线、内存开销、调优参数都不同,按实测选。
小结#
Shenandoah 是 Red Hat 主导的并发整理型低延迟 GC,用转发指针 + 读屏障实现并发转移,与 ZGC 的染色指针路线互补。它是否可选,往往取决于 JDK 发行版——Red Hat/Corretto 等有,Oracle/多数本机构建无。JDK 25 完成分代化(JEP 521),与现代 ZGC 同处低延迟第一梯队。