shenandoah的核心价值在于将标记、回收和整理全部并发化,仅四次亚毫秒级stw(初始/最终标记、初始/最终引用更新),通过satb快照、brooks转发指针、读/写屏障及连接矩阵实现毫秒级停顿且与堆大小无关。

Shenandoah 的核心价值在于把原本必须“暂停用户线程”才能完成的标记、回收(即对象复制)和整理(即引用更新)三大关键动作,全部转为与应用线程并发执行。它不靠堆大小缩放停顿,而是靠阶段拆分 + 屏障控制 + 数据结构优化来实现毫秒级 STW。
并发标记:边跑边画“存活地图”
这个阶段从 GC Roots 出发,遍历整个对象图,给所有可达对象打上“存活”标记。它不是一口气扫完再停,而是边运行应用、边用多个 GC 线程协同扫描。
- 依赖 SATB(Snapshot-At-The-Beginning)快照机制:在初始标记结束那一刻拍下引用关系快照,后续新增/修改的引用不影响本次标记结果
- 使用读屏障(Load Barrier)捕获对象访问,确保不会漏标正在被读取但尚未标记的对象
- 标记过程本身无 STW,耗时取决于存活对象数量和图深度,与堆总大小无关
并发回收:边用边搬,对象双副本共存
这是 Shenandoah 区别于 G1 的最关键技术突破——对象复制不再等 STW,而是在用户线程持续运行中完成。
- GC 线程将回收集(Collection Set)中存活对象复制到新 Region;同时,原对象仍保留在旧位置,供用户线程继续读写
- 每个对象头插入 Brooks 转发指针,指向其新副本地址;写屏障(Store Barrier)拦截每次字段赋值,确保写操作落在新副本上
- 若用户线程读取一个已复制对象,会通过转发指针自动跳转到新副本,保证语义一致
并发整理:分两步更新引用,避免全局锁
对象搬走了,所有指向它的引用都得改——但不能等所有线程停下再统一改。Shenandoah 把引用更新也拆成并发阶段:
- 初始引用更新(STW,极短):只更新 GC Roots 直接引用的对象(如栈帧、寄存器、静态字段),停顿仅 0.1ms 级别
- 并发引用更新:GC 线程遍历堆中所有对象,逐个检查其字段是否指向回收集中的旧地址;是则通过转发指针修正为新地址
- 最终引用更新(STW,极短):收尾校验,处理并发更新期间可能遗漏的少量根引用
整个流程九个阶段中,真正需要 STW 的只有初始标记、最终标记、初始/最终引用更新这四次,且每次均控制在亚毫秒级。其余五个阶段全为并发,因此整体停顿时间稳定在 1~10ms 量级,与堆是 2GB 还是 200GB 无关。











