MemorySegment 不能直接实现跨进程内存映射,必须通过 FileChannel.map() 创建 MappedByteBuffer 后调用 MemorySegment.ofMappedBuffer() 接入 mmap 区域;该段不绑定 Arena,生命周期由 ByteBuffer 控制,需显式 force() 并等待 GC 释放。

不能直接用 MemorySegment 实现跨进程内存映射——它本身不提供进程间共享能力,必须配合 FileChannel.map() + MemorySegment.ofMappedBuffer() 才能安全接入 mmap 区域。
MemorySegment.ofMappedBuffer() 是唯一合法入口
Java 没有原生“跨进程堆外内存”抽象。所谓“跨进程”,实际依赖操作系统级的内存映射文件(mmap)。JDK 22 中唯一受支持的、能将 mmap 区域转为 MemorySegment 的方式是 MemorySegment.ofMappedBuffer(),且该方法只接受 MappedByteBuffer 参数。
-
MappedByteBuffer必须由FileChannel.map()创建,不能是ByteBuffer.allocateDirect()或其他来源 - 映射模式必须为
FileChannel.MapMode.READ_WRITE或READ_ONLY,PRIVATE(copy-on-write)不适用于跨进程同步 - 映射文件需由多个进程以相同路径、相同打开方式(如都用
RandomAccessFile)访问,否则段视图无法对齐
越界/并发写入会立即抛异常,而非静默破坏
和 Unsafe 不同,MemorySegment 对 mmap 区域的每次 get()/set() 都强制校验偏移量是否在映射范围内:
-
segment.get(ValueLayout.JAVA_INT, 1024)若超出MappedByteBuffer容量,立刻抛IndexOutOfBoundsException - 即使底层 mmap 区域被另一进程截断或 unmap,当前 JVM 再次访问时会抛
IllegalStateException(“segment is closed”),而不是触发 SIGSEGV - 但注意:它不解决多进程间的竞态问题——
get()和set()本身不是原子操作,仍需外部同步(如文件锁、信号量)
Arena 不能用于管理 mmap 段的生命周期
MemorySegment.ofMappedBuffer() 返回的段**不绑定任何 Arena**,其生命周期完全由底层 MappedByteBuffer 控制。这意味着:
- 不能用
try (Arena arena = ...)包裹它来自动释放——调用arena.close()对该段无效 - 也不能调用
segment.close()(JDK 22 已移除该方法),更不能手动调用Cleaner.clean() - 正确释放方式只有:显式调用
mappedBuffer.force()(确保脏页刷盘),然后丢弃对该MappedByteBuffer的所有引用,等待 GC 触发其 Cleaner(虽然已 deprecated,但目前仍是唯一机制)
ValueLayout 强制对齐与类型语义,避免手算偏移错误
跨进程共享结构体(如 C 的 struct msg { int id; long ts; char data[64]; })时,ValueLayout 能消除手工计算偏移的隐患:
- 用
MemoryLayout.structLayout(ValueLayout.JAVA_INT.withName("id"), ValueLayout.JAVA_LONG.withName("ts"), MemoryLayout.sequenceLayout(64, ValueLayout.JAVA_BYTE).withName("data"))声明布局 - 再通过
layout.varHandle(PathElement.groupElement("id"))获取类型安全的VarHandle,读写自动按平台 ABI 对齐 - 若另一进程用不同编译器(如 MSVC vs GCC)生成结构体,需显式指定
byteOrder(ByteOrder.LITTLE_ENDIAN),否则get()可能读出乱值
真正容易被忽略的是:mmap 文件的磁盘空间必须预先分配足(例如用 channel.truncate(size)),否则不同进程映射同一文件时,capacity() 可能不一致,导致 MemorySegment 校验失败或部分区域不可见。

















