Shenandoah使用连接矩阵替代G1的记忆集以降低跨Region引用维护开销:前者为全局二维布尔表,仅记录Region级引用关系,避免伪共享和高内存占用;后者为每个Region独立维护的哈希表,精度高但开销大。
Shenandoah 与 G1 在跨 Region 引用维护和并发标记阶段既有明显继承关系,也有关键设计取舍——前者重在降低开销、支持全并发,后者更侧重分代精度与吞吐平衡。
G1 使用每个 Region 独立维护的“记忆集”(Remembered Set),本质是哈希表结构,记录本 Region 中对象指向其他 Region 的引用。这种细粒度设计能精准定位脏卡,但代价高:需占用堆内存 10%–20%,且频繁更新易引发伪共享和写屏障开销。
Shenandoah 则完全摒弃记忆集,改用全局“连接矩阵”(Connection Matrix)——一张二维布尔表,行与列均代表 Region。若 Region A 中有任意对象引用 Region B 中的对象,就在 matrix[A][B] 标记为 true。
两者都采用三色标记法 + SATB(Snapshot-At-The-Beginning)快照机制,阶段划分高度相似:初始标记(STW)、并发标记(用户线程运行中遍历对象图)、最终标记(短暂 STW,处理剩余增量引用)。
区别在于标记结果的后续用途:
连接矩阵 + 无分代 + 并发整理,让 Shenandoah 把 STW 时间压到毫秒级(典型 5–50ms),尤其适合堆大(数十 GB 至 TB 级)、延迟敏感的场景,如实时风控、高频交易网关。
G1 虽在 JDK 17+ 后持续优化,但其记忆集维护和部分 STW 回收阶段仍导致停顿更长(10–500ms),更适合对吞吐要求略高、能接受中等延迟的通用服务。
两者在标记逻辑上共享大量代码,说明 Shenandoah 确实是 G1 的演进而非推倒重来——只是把“如何减少停顿”放在了绝对优先位置。