bigywater.tech
Java · 296

Shenandoah Barrier:并发压缩如何保持引用可用

Shenandoah 靠一组屏障让回收和应用线程并发。每种模式到底开了哪些屏障?能单独关掉吗?它们在读写引用时到底有多贵?这篇逐项检查、逐项测量。

6 种
模式配置里的屏障开关组合(JDK 21 satb / iu / passive,JDK 27 satb / generational / passive)
≤ 0.3 ns
本测试里 Shenandoah 屏障对每次引用存储的额外耗时
0.361 ms
分配密集型负载下观察到的最大暂停(JDK 27 satb,Final Mark)

屏障开关

实测 · 本机执行输出
  JDK 21 satb            SATB=true IU=false LoadRef=true CAS=true Clone=true StackWatermark=true
  JDK 21 iu              SATB=false IU=true LoadRef=true CAS=true Clone=true StackWatermark=true
  JDK 21 passive         SATB=false IU=false LoadRef=false CAS=false Clone=false StackWatermark=false
  JDK 27 satb            SATB=true LoadRef=true Clone=true Card=false
  JDK 27 generational    SATB=true LoadRef=true Clone=true Card=true
  JDK 27 passive         SATB=false LoadRef=false Clone=false Card=false
  关闭任一屏障(satb 模式):每个都被拒绝(见 JDK 21 与 27 上的错误信息):
    JDK 21 -XX:-ShenandoahSATBBarrier → Error: GC mode needs -XX:+ShenandoahSATBBarrier to work correctly
    JDK 21 -XX:-ShenandoahLoadRefBarrier → Error: GC mode needs -XX:+ShenandoahLoadRefBarrier to work correctly
    JDK 27 -XX:-ShenandoahSATBBarrier → Error: GC mode needs -XX:+ShenandoahSATBBarrier to work correctly
    JDK 27 -XX:-ShenandoahLoadRefBarrier → Error: GC mode needs -XX:+ShenandoahLoadRefBarrier to work correctly

运行时代价

实测 · 本机执行输出
  JDK 21 satb            链表读  57.63 ns/节点   引用存储   1.74 ns/次   (读 [56.58, 64.67, 57.63]  存 [1.74, 1.74, 1.74])
  JDK 21 iu              链表读  58.64 ns/节点   引用存储   1.64 ns/次   (读 [58.64, 59.73, 56.5]  存 [1.64, 1.64, 1.64])
  JDK 21 passive         链表读   57.5 ns/节点   引用存储   1.48 ns/次   (读 [56.28, 58.28, 57.5]  存 [1.49, 1.46, 1.48])
  JDK 27 satb            链表读  57.31 ns/节点   引用存储   1.34 ns/次   (读 [57.2, 57.67, 57.31]  存 [1.33, 1.34, 1.37])
  JDK 27 generational    链表读  58.32 ns/节点   引用存储   1.49 ns/次   (读 [58.39, 58.32, 57.29]  存 [1.49, 1.49, 1.51])
  JDK 27 passive         链表读  57.73 ns/节点   引用存储   1.34 ns/次   (读 [58.64, 57.73, 56.72]  存 [1.34, 1.32, 1.35])
  JDK 21 G1(对照)          链表读  57.55 ns/节点   引用存储   3.22 ns/次   (读 [62.38, 57.55, 56.37]  存 [4.36, 3.22, 3.05])
  JDK 27 G1(对照)          链表读  56.41 ns/节点   引用存储   1.66 ns/次   (读 [59.62, 56.41, 55.72]  存 [1.66, 1.67, 1.64])

暂停

实测 · 本机执行输出
  JDK 21 satb            暂停 984 次;Final Mark 246 次 最大 0.334 ms;Final Update Refs 246 次 最大 0.036 ms;Init Mark 246 次 最大 0.073 ms;Init Update Refs 246 次 最大 0.008 ms;6 秒分配 9.7 百万次
  JDK 21 iu              暂停 1008 次;Final Mark 252 次 最大 0.231 ms;Final Update Refs 252 次 最大 0.030 ms;Init Mark 252 次 最大 0.080 ms;Init Update Refs 252 次 最大 0.030 ms;6 秒分配 10.0 百万次
  JDK 27 satb            暂停 570 次;Final Mark 99 次 最大 0.361 ms;Final Update Refs 52 次 最大 0.064 ms;Init Mark 419 次 最大 0.038 ms;6 秒分配 24.0 百万次
  JDK 27 generational    暂停 468 次;Final Mark 118 次 最大 0.185 ms;Final Update Refs 89 次 最大 0.075 ms;Init Mark 261 次 最大 0.029 ms;6 秒分配 18.8 百万次

分步演示

Shenandoah 的屏障
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12 与 JDK 27(Homebrew openjdk),Apple Silicon;满足 diagnostic / experimental 选项解锁条件
    • 没有验证:各屏障生成的机器码、对象的转发指针(Brooks 指针)在不同版本里的位置与存在性、屏障在并发标记 / 疏散进行中的慢速路径开销(本测试的循环发生时没有 GC 在进行)、Linux 与 x86、Shenandoah 在大堆下的行为
    京ICP备2026067502号-1京公网安备11010502063487号