bigywater.tech
Java · 208

GCLocker:JNI 临界区为什么会拖住垃圾回收

JNI 里的数组临界区为什么会拖住整个 JVM 的垃圾回收?在新版本里还成立吗?这篇用一个 clang 编译的 JNI 函数持有临界区 600 ms,在 JDK 21 与 27、G1 与 Parallel 上各量一次(JNI 以外的堆外内存见前面的篇目)。

610.1 ms
JDK 21 G1:持有 JNI 临界区时分配线程最长的一次分配
0.6 ms
JDK 27 G1:同样的场景
604.0 ms
JDK 27 Parallel:仍然被卡住

JNI 临界区与 GC

实测 · 本机执行输出
  (JNI 方法是我用 clang 编译的 C 函数,usleep 模拟本地代码在临界区里的耗时)
JDK / 回收器               临界区 (critical)        最长一次分配(ms)      分配次数   GC 次数    最长 GC 停顿(ms)
JDK 21 G1             否(GetByteArrayElements 拷贝)               1.4    274165    2980             1.3
JDK 21 G1                            是             610.1    164005    1782             0.5
JDK 21 Parallel       否(GetByteArrayElements 拷贝)               0.4    424006    3449             0.3
JDK 21 Parallel                      是             592.4    181839    1473             1.0
JDK 27 G1             否(GetByteArrayElements 拷贝)               1.3    273446    2972             1.0
JDK 27 G1                            是               0.6    274440    2983             0.5
JDK 27 Parallel       否(GetByteArrayElements 拷贝)               0.8    425288    3458             0.6
JDK 27 Parallel                      是             604.0    254365    2068             0.4

分步演示

GCLocker
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12 与 27,macOS,-Xmx256m -Xmn32m;JNI 函数用 clang 编译,usleep 模拟临界区里的耗时
    • 没有验证:JDK 22 到 26 之间 G1 区域钉住(pinning)是从哪个版本开始的、ZGC 与 Shenandoah 的表现、GCLocker 触发的 "GCLocker Initiated GC" 日志、Unsafe/Critical 在 FFM 里的对应(MemorySegment 的 critical 链接选项);只测了这四种组合各一次
    京ICP备2026067502号-1京公网安备11010502063487号