shenandoah 用显式读屏障+brooks转发指针,每次读取前检查对象头fwdptr并跳转;zgc 用染色指针+转发表,仅通过地址高位颜色位判断视图并映射,不改对象结构、无额外字段访问。

读屏障实现方式不同
Shenandoah 的读屏障是显式插入的运行时检查逻辑,每次对象引用读取(如 obj.field)前,JVM 会调用 ShenandoahBarrierSet::read_barrier() 方法。该方法判断对象是否已被转发(即堆中存在 forwarded 状态),若存在,则通过 Brooks 指针跳转到新地址;否则直接返回原对象。这个过程依赖对对象头中 forwarding pointer 字段的原子读取与解引用,开销稳定但需每个读操作介入。
ZGC 的读屏障不依赖额外字段读取,而是利用**染色指针(Colored Pointers)** 的地址位编码。ZGC 将对象地址的高几位(如第42–44位)用作标记位,表示当前指针处于 Mark0、Mark1 或 Remapped 视图。读屏障仅需检查指针颜色位,再根据视图映射规则决定是否重映射访问地址。它不访问堆内存,也不修改对象结构,因此读操作延迟更低、更轻量。
转发机制底层设计根本不同
Shenandoah 使用 **Brooks 转发指针**:在每个对象头前(或对象头内)额外开辟一个 word 大小的 slot,称为 fwdptr,初始指向自身。当对象被并发移动时,GC 线程通过 CAS 原子更新该指针为新地址;后续所有对该对象的读/写访问,都先经由该指针间接跳转。这意味着对象结构被扩展,且所有访问路径必须经过读/写屏障转发。
ZGC 不使用转发指针,而是采用 **转发表(Forwarding Table)+ 地址视图切换**:对象本身不带任何转发字段,移动后旧地址仍可访问——因为 ZGC 利用操作系统 mmap 的多重内存映射能力,将同一物理内存页同时映射到多个虚拟地址空间(Mark0/Mark1/Remapped)。当对象被复制后,ZGC 更新转发表中对应项,并在后续读屏障中根据指针颜色自动切换到 Remapped 视图访问新副本。对象原始内存布局完全不变。
对内存与硬件的依赖差异明显
Shenandoah 对内存无特殊要求,兼容 32 位和 64 位 JVM,可在任意操作系统上运行。其 Brooks 指针方案只增加少量对象头开销(通常 4–8 字节),但需要读/写屏障全覆盖,对 JIT 编译优化有一定限制。
ZGC 强制要求 64 位系统,且因染色指针需复用地址高位,无法启用压缩 OOP(-XX:+UseCompressedOops 必须关闭)。它依赖现代 CPU 的 TLB 性能与操作系统的高效内存映射支持,在容器或内存受限环境(如 512MB 容器)中需谨慎评估页表压力。不过,它避免了对象头膨胀,更适合对内存敏感或强依赖对象密度的场景。
并发整理阶段的屏障触发时机有别
Shenandoah 在整个 GC 周期的多数阶段(包括初始标记、并发标记、并发清理、并发整理)都持续启用读屏障,只要堆中存在已转发对象,读操作就必须检查 fwdptr。这意味着即使在 GC 完成后,若仍有未清理的转发状态,屏障仍起作用。
ZGC 的读屏障仅在对象处于 Mark0 或 Mark1 视图时生效;一旦进入 Remapped 视图,所有指针已“就位”,屏障逻辑可短路跳过。ZGC 更倾向于将屏障开销集中在 GC 活跃期,而非长期驻留。










