shenandoah与zgc并发标记均采用三色标记法且全程并发,但zgc依赖染色指针+读屏障触发标记,shenandoah采用satb+写屏障与连接矩阵;zgc需linux≥4.14支持大页重映射,shenandoah需禁用透明大页;zgc标记进度具随机性,shenandoah更可预测。

Shenandoah 和 ZGC 在并发标记阶段都采用三色标记法(白-灰-黑),目标一致:遍历对象图、识别全部可达对象,且全程与用户线程并发执行。但实现机制和底层依赖存在本质差异,直接影响稳定性、兼容性和运行时行为。
标记触发方式与根扫描范围不同
两者初始标记(STW 阶段)均从 GC Roots 出发,但后续并发标记的推进逻辑有区别:
- ZGC 的并发标记以“染色指针”为载体:每个对象指针高 bits 编码其状态(如 marked0/marked1/remapped),标记过程通过读屏障(Load Barrier)在每次对象访问时触发状态检查与传播;只要对象被读取,就可能推动标记前进。
- Shenandoah 则依赖传统写屏障 + SATB(Snapshot-At-The-Beginning)快照机制:在初始标记结束瞬间记录堆快照,之后所有新引用写入都通过写屏障捕获并加入增量更新队列;并发标记线程独立遍历快照中的灰色对象,不依赖每次读操作触发。
跨 Region 引用处理策略不同
标记需准确追踪跨区域引用,二者采用完全不同的数据结构支撑:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- ZGC 不维护显式跨页引用关系表,靠染色指针+内存重映射隐式保障——对象移动后,旧地址仍可通过操作系统页表重定向到新位置,标记线程访问任意地址都能获得当前有效对象,无需额外查表。
- Shenandoah 使用连接矩阵(Connection Matrix):一个稀疏二维数组,标记 Region A → Region B 是否存在引用。并发标记过程中,若发现某 Region 内对象指向外部 Region,需查该矩阵确认是否已覆盖该跨区边;未覆盖则加入待扫描队列,避免漏标。
对 OS 和硬件能力的依赖程度不同
这种差异直接体现在部署约束上:
- ZGC 的并发标记高度依赖操作系统支持大页重映射(Linux ≥ 4.14 + userfaultfd),读屏障必须配合内核页表机制才能安全跳转;若内核不满足,ZGC 会降级或初始/最终标记时间飙升。
- Shenandoah 的并发标记纯软件实现,不依赖特定内核特性,但需禁用透明大页(THP),否则 Brooks 指针转发与 THP 合并行为冲突,可能导致标记中断或错误。
暂停敏感性与标记进度可控性不同
虽然都宣称“停顿与堆大小无关”,但实际场景中表现有细微差别:
- ZGC 并发标记本身无 STW,但若分配速率过高导致内存耗尽,可能触发 Allocation Stall(短暂阻塞分配),间接影响标记线程调度;标记进度由读屏障触发密度决定,具有随机性。
- Shenandoah 在最终标记阶段会做 SATB 剩余扫描并选定回收集,这个阶段有短暂停顿;其并发标记进度更可预测,因为由专用标记线程按 Region 粒度推进,受 Mutator 分配压力影响较小。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










