bigywater.tech
Java · 304

Arena 与 MemorySegment:堆外资源如何成组释放

Arena 决定一块堆外内存的寿命。四种 Arena 差在哪?段能多大?堆段与原生段有哪些差别?内存映射文件怎么用?这篇逐项运行。

3076 MB
allocate(3 GB) 之后 RSS 的增量(整段被清零)
2 个
不能手动 close 的 Arena:global 与 auto
-1 / 1000
mismatch 在两段相同与改动第 1000 字节之后的返回值

四种 Arena

实测 · 本机执行输出
  1 四种 Arena:
    global:可写读 16 字节;scope().isAlive()=true
    global.close():UnsupportedOperationException: Attempted to close a non-closeable session
    auto:isAlive=true
    auto.close():UnsupportedOperationException: Attempted to close a non-closeable session
    confined:isAlive=true;close 之后 isAlive=false
    shared:isAlive=true;close 之后 isAlive=false
    confined 在其它线程 close():WrongThreadException: Attempted access outside owning thread
    shared 在其它线程 close():成功
    同一个 Arena 里再分配 3 次,地址是否各不相同:true

分配规则

实测 · 本机执行输出
  2 新分配的 1 MB 段是否全为 0:true;对齐:allocate(1, 64) 的地址 % 64 = 0;allocate(1, 4096) 的地址 % 4096 = 0
    allocate(8, 3):IllegalArgumentException: Invalid alignment constraint : 3
    allocate(-1):IllegalArgumentException: The provided allocation size is negative: -1

3 GB 的段

实测 · 本机执行输出
  3 allocate(3 GB):byteSize=3221225472;RSS 增加 3076 MB(allocate 把整段清零,页被真正占用)
    写最后一个字节 9;RSS 增加 3082 MB
    3 GB 段 asByteBuffer():IllegalStateException: Segment is too large to wrap as ByteBuffer. Size: 3221225472
    3 GB 段 toArray(JAVA_BYTE):IllegalStateException: Segment is too large to wrap as byte[]. Size: 3221225472
    asSlice(1 GB, 16).byteSize()=16
    asSlice 越过末尾:IndexOutOfBoundsException: Out of bound access on segment MemorySegment{ kind: native, address: 0x7c40800000, byteSize: 3221225472 }; attempting to get slice of length
    Arena 关闭后立即 RSS 相对开始增加 3084 MB
    关闭 2 秒并 System.gc() 之后 RSS 相对开始增加 3086 MB

批量操作与堆段

实测 · 本机执行输出
  4 128 MB:fill 4 ms;MemorySegment.copy 原生→堆 5 ms;copyFrom 2 ms;System.arraycopy 堆→堆 13 ms;mismatch(相同内容)返回 -1,11 ms
    改动堆数组第 1000 字节后 mismatch = 1000
    堆段:isNative=false,heapBase 存在=true;原生段 isNative=true
    堆段 address()=0(只是数组内偏移)
    堆段 reinterpret(16):UnsupportedOperationException: Not a native segment
    int[] 段 byteSize=16;按 JAVA_INT 读第 2 个=2;按 JAVA_BYTE 读第 4 个字节(小端的 2)=2
    int[] 段上读 JAVA_LONG offset 0:IllegalArgumentException: Target offset 0 is incompatible with alignment constraint 8 (of j8) for segment MemorySegment{ kind: heap, heapBase: [I@3d646c37, address: 0
    int[] 段上读 JAVA_LONG_UNALIGNED offset 4:12884901890

映射文件

实测 · 本机执行输出
  5 映射文件 4096 字节:isMapped=true,isNative=true;写入 magic 与字符串并 force()
    映射 3 GB:byteSize=3221225472,文件长度变为 3072 MB
    Arena 关闭后用普通 IO 读回前 4 字节(小端):bebafeca;字符串位置读回:mapped-by-FFM

分步演示

Arena 与 MemorySegment
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 27,macOS arm64,--enable-native-access=ALL-UNNAMED;只在 JDK 27 上做(JDK 21 的 API 名字不同)
    • 没有验证:Linux 上的 RSS 行为、关闭 Arena 后 RSS 不降的原因、ofAuto 由 GC 回收的时机、MemorySegment 性能与 ByteBuffer / Unsafe 的对比(批量操作的几个毫秒数是单次观察,不是基准)、大页与 madvise、Windows 的映射语义
    京ICP备2026067502号-1京公网安备11010502063487号