Shenandoah 通过对象头中的转发指针实现并发压缩整理,GC 线程复制对象并更新指针,用户线程经读屏障自动重定向访问;无需批量修正引用,仅 Roots 更新需短暂 STW,停顿控制在 10ms 内。

Shenandoah 实现并发压缩整理的核心,是靠每个对象头里嵌入的转发指针(Brooks Pointer),而不是直接修改所有引用。它不等用户线程停顿,就能安全地移动对象、更新访问路径。
转发指针怎么工作
每个对象在 Shenandoah 下都带一个额外字段,初始时指向自己;当该对象被选中要移动时,GC 线程先在目标 Region 中复制一份新副本,再把原对象头里的转发指针更新为新地址。此后任何对这个对象的读写,都会通过转发指针自动跳转到新位置。
- 用户线程访问对象时,JVM 在读屏障(Read Barrier)拦截下检查转发指针,若已设置,就直接重定向到新地址
- 写操作同样经过读屏障,保证不会误写旧内存区域
- 原对象所在内存暂不释放,直到所有引用完成重定向且确认无访问
为什么不需要改遍所有引用
传统整理算法必须暂停应用,遍历整个堆和栈,逐个修正指向旧地址的引用——这代价极高。Shenandoah 把“引用更新”这个动作拆解了:
- 堆内引用:不立即批量更新,而是靠读屏障+转发指针,在每次访问时惰性重定向
- GC Roots 引用(如栈帧、静态变量):在最终引用更新阶段 STW 完成,但只涉及少量根节点,耗时极短(通常 sub-millisecond)
- 转发指针本身是原子写入的,确保多线程访问下的一致性
并发回收阶段的关键配合
转发指针不是孤立机制,它和几个并发阶段协同实现低延迟:
- 并发回收:GC 线程一边复制存活对象,一边更新其转发指针;用户线程继续运行,靠读屏障透明访问
- 并发引用更新:并非重写全部引用,而是按内存物理地址顺序扫描堆中引用类型字段,把仍指向旧地址的引用批量改为新地址(这是优化项,非必需)
- 初始/最终引用更新:两个极短 STW 阶段,仅用于同步线程状态和处理 Roots,不承担主引用更新压力
实际效果与限制
转发指针让 Shenandoah 能在应用持续运行时完成对象移动和空间整理,绝大多数 GC 暂停控制在 10ms 以内,且基本不随堆大小增长。
- 开销在于每次对象访问多一次指针间接跳转(由读屏障引入),现代 CPU 分支预测可缓解
- 要求 JVM 支持读屏障插入(HotSpot 自 JDK 12 起完整支持 Shenandoah)
- 大对象(Humongous Object)不参与转发指针机制,走单独 H-Region 回收流程

















