shenandoah 收集器使用转发指针(forwarding pointer)而非 brooks pointers:对象头嵌入8字节字段存新地址,读屏障检查并重定向访问,避免全局引用更新,仅根集合更新需短暂stw,停顿可控在10ms内。

Shenandoah 收集器并不使用 Brooks Pointers,而是采用**转发指针(Forwarding Pointer)**机制实现并发对象移动和压缩整理。这个常见误解源于早期文档或类比描述,但技术实现上二者有本质区别。
转发指针不是 Brooks 指针
Brooks 指针是一种间接寻址设计:每个对象被改造成“指针容器”,所有字段外移至堆外结构,对象本体只保留一个指向该结构的指针——每次字段访问都要两次解引用,开销稳定且不可省略。
Shenandoah 的转发指针是直接嵌入对象头的 8 字节字段,初始时指向对象自身;当对象被复制后,GC 线程用 CAS 原子更新为新地址。它不改变对象布局,不引入额外 indirection 层,JIT 编译器友好,字段访问仍为单次加载(仅在被移动后首次访问时多一次条件跳转)。
转发指针如何支撑并发移动
核心靠读屏障(Read Barrier)与对象头中转发地址协同工作:
- 用户线程执行
obj.field时,JVM 自动插入读屏障,检查对象头转发指针是否非空 - 若已设置新地址,屏障立即重定向访问目标,后续操作直接作用于新副本
- 写操作同样受读屏障保护,避免误写旧内存区域
- 未被移动的对象,转发指针仍指向自身,逻辑完全兼容,无需分支判断
为什么能避免全局引用更新
传统整理式 GC 必须 STW 扫描全堆+栈+方法区,逐个修正旧引用——随机访存、缓存不友好、耗时随堆增长。
Shenandoah 将引用修正“下沉”到每次访问时:
- 堆内普通引用:靠读屏障实时重定向,无需立即更新
- GC Roots(栈帧、静态变量、JNI 引用):仅在最终阶段短暂 STW 更新,通常低于 1ms
- Concurrent Update References 阶段可选,用于批量优化,非正确性必需
实际效果与代价
转发指针换来极短停顿:初始标记和最终根更新两个 STW 环节,总时长基本控制在 10ms 内,且与堆大小无关。
代价包括:
- 每个对象增加 8 字节内存开销
- 每次对象字段读取引入一次轻量分支预测 + 条件跳转(现代 CPU 可高效处理)
- 要求 JVM 支持读屏障插入(HotSpot 自 JDK 12 起完整支持)
大对象(Humongous Object)不参与该机制,走独立 H-Region 回收路径。











