shenandoah 通过 brooks 转发指针与读写屏障实现并发整理,仅四次亚毫秒 stw,解耦对象移动与引用更新,用连接矩阵替代卡表,支持大堆低停顿内存管理。

Shenandoah 实现并发流式内存整理与低停顿,核心在于把“移动对象”和“更新引用”这两件事彻底解耦,并全部推向并发执行——它不等所有线程停下来再统一搬数据、改指针,而是让 GC 线程边搬边记,让用户线程边跑边自动跳转。
靠转发指针实现对象访问的实时重定向
每个对象头里嵌入一个 Brooks 转发指针(fwdptr),初始指向自身。一旦该对象被选入回收集,GC 线程就在目标 Region 复制新副本,然后原子更新原对象头中的 fwdptr 指向新地址。
- 用户线程每次读取对象前,JVM 通过读屏障检查 fwdptr;若已设置,就直接跳转到新位置,无需等待全局引用更新
- 写操作同样受读屏障拦截,确保字段修改落在新副本上,旧内存保持只读直至确认无访问
- 对象迁移过程对应用完全透明,不存在“访问到一半被挪走”的风险
分阶段处理引用更新,避免全局扫描
传统整理必须 STW 扫全堆+全栈来修正所有旧地址引用,而 Shenandoah 把引用更新拆成三块:
- 初始引用更新(STW):仅处理 GC Roots(如 Java 栈帧、静态变量、JNI 引用),耗时通常低于 0.1ms
- 并发引用更新:GC 线程按物理地址顺序遍历堆中引用字段,批量将仍指向旧地址的引用改为新地址——这是优化项,非强制路径,即使跳过也不影响正确性
- 最终引用更新(STW):极短收尾校验,修补并发过程中可能遗漏的少量根级引用
标记与回收全程并发,且不依赖堆大小
Shenandoah 的九个阶段中,只有四次亚毫秒级 STW(初始/最终标记、初始/最终引用更新),其余全部并发:
- 并发标记基于 SATB 快照:在初始标记结束瞬间冻结引用关系视图,后续新增或修改引用不影响本轮结果
- 并发回收阶段,GC 线程复制存活对象的同时更新 fwdptr,用户线程持续运行,读写均经屏障自动适配
- 没有分代设计,不区分新生代老年代,所有 Region 统一参与整理,简化了跨代记忆集维护开销
连接矩阵替代卡表,降低元数据开销
为支持跨 Region 引用追踪,Shenandoah 用稀疏二维连接矩阵(Connection Matrix)代替 G1 的卡表(Remembered Set):
- 矩阵行列表示 Region 编号,矩阵项为布尔值,表示“Region A 中的对象是否引用了 Region B 中的对象”
- 空间占用更小,更新成本更低,尤其适合大堆场景下频繁的跨区引用探测
- 配合读/写屏障,能快速定位需扫描的 Region 对,提升并发标记与回收效率











