JEP 370:JDK 14的外部内存访问API | Perforce旗下JRebel
随着JDK发布节奏的加快,几乎每周都会有新的JDK增强提案(JEP)发布。本文将聚焦于JEP 370:外部内存访问API(Foreign-Memory Access API,孵化器模块),这是JDK 14中的一项新特性。我们将深入探讨该API的工作原理,并详细介绍其三个核心抽象:内存段(MemorySegment)、内存地址(MemoryAddress)和内存布局(MemoryLayout)。
JEP 370:外部内存访问API(孵化器模块)
JEP 370的核心目标是为Java程序提供访问Java堆之外外部存储器的能力。根据JEP的说明,引入这一API的主要原因包括:
- 支持跨多个进程共享内存。
- 通过将文件映射到内存,实现内存内容的序列化和反序列化。
尽管这一提案的动机明确,但目前的实现尚未提供一种在进程间轻松共享内存的方法。
外部内存访问API是否带来了新功能?
简单来说:没有。许多现有的Java库已经能够访问外部内存,因此JDK 14中新增的外部内存访问API并未直接为这些库增加新功能。然而,该API提供了一种更受支持且更具互操作性的方法,能够在JVM中以更安全的方式处理外部内存,而无需依赖外部库。
在现有的JDK中,类似功能通常通过以下方式实现:
- ByteBuffer API:自Java 1.4以来一直存在。
- JNI(Java Native Interface):需要为每种架构实现本地代码。
- Unsafe API:允许访问任意内存位置,但不受官方支持。
相比之下,外部内存访问API的引入显得尤为必要。
JEP 370与巴拿马项目的关系
JEP 370是巴拿马项目的一部分,该项目旨在提升JVM与非Java API之间的互操作性。通过引入一种可靠且高效的方式与外部内存交互,JEP 370进一步推动了巴拿马项目的目标,同时为未来增强外部函数接口奠定了基础。
外部内存访问API的工作原理
外部内存访问API引入了三个核心抽象:
- MemorySegment:表示一段连续的内存区域。
- MemoryAddress:表示内存段中的具体位置。
- MemoryLayout:以语言中立的方式定义内存段的布局。
由于该API位于孵化器模块中,使用时需要在module-info.java文件中添加jdk.incubator.foreign模块,或者通过--add-modules JVM参数启用。
使用内存段(MemorySegment)
内存段是对连续内存区域的抽象,既可以由堆内存支持,也可以由堆外内存支持。通过工厂方法,可以创建由基元数组、ByteBuffer或文件的内存映射区域支持的内存段,或者分配新的堆外内存段。
为了确保资源的及时释放,MemorySegment实现了AutoCloseable接口,因此可以与try-with-resources一起使用。当内存段被关闭后,再次访问将抛出IllegalStateException。
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
// 使用内存段
}
内存段绑定到创建时的线程。如果需要在其他线程中使用,需要通过acquire()方法获取一个新段。在关闭所有获取的内存段之前,原始内存段无法关闭。
使用内存地址(MemoryAddress)
MemoryAddress表示内存段中的偏移量,通常通过调用segment.baseAddress()方法获取。尽管内存地址是线程安全的,但在多线程环境中使用时,仍需先获取关联的内存段。
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
MemoryAddress address = segment.baseAddress();
}
内存地址通常与VarHandle配合使用,以访问底层数据。例如,以下代码展示了如何以大端字节序写入整数数据:
VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN);
for (int i = 0; i < 32; i++) {
handle.set(segment.baseAddress().addOffset(i * Integer.BYTES), 1 << i);
}
使用内存布局(MemoryLayout)
通过偏移量直接访问内存段可能并不直观。MemoryLayout提供了一种语言中立的方式来描述内存布局,例如声明小端字节序的32位值或本机顺序的64位值。
SequenceLayout layout = MemoryLayout.sequenceLayout(10, MemoryLayouts.JAVA_INT);
更复杂的数据结构也可以通过MemoryLayout描述。例如,一个包含两个整数的点结构可以定义为:
GroupLayout pointLayout = MemoryLayout.structLayout(
MemoryLayouts.JAVA_INT.withName("x"),
MemoryLayouts.JAVA_INT.withName("y")
);
此外,内存布局支持嵌套。例如,一个三角形可以表示为三个点的序列:
SequenceLayout triangleLayout = MemoryLayout.sequenceLayout(3, pointLayout);
使用VarHandle访问单个元素
VarHandle允许通过内存布局访问具体的内存元素。例如,以下代码展示了如何访问三角形中第二个点的x和y坐标:
VarHandle varX = triangleLayout.varHandle(int.class,
MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.groupElement("x")
);
VarHandle varY = triangleLayout.varHandle(int.class,
MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.groupElement("y")
);
varX.set(triangleSegment.baseAddress(), 1, 10);
varY.set(triangleSegment.baseAddress(), 1, 30);
展望未来
外部内存访问API为描述和访问内存段提供了更有序的工具。未来的改进可能包括支持将记录或内联类直接映射到MemoryLayout,从而简化从内存映射文件读取和写入结构化数据的过程。
总结
JEP 370通过引入外部内存访问API,为Java提供了一种更高效、更安全的方式来与外部内存交互。这不仅提升了JVM的互操作性,也为未来的功能扩展奠定了基础。随着该API的不断完善,Java开发者将能够更轻松地处理复杂的内存操作。
原文链接: https://www.jrebel.com/blog/jep-370-foreign-memory-access-api
最新文章
- DRF库详解:用Django轻松搭建功能强大的API服务
- 一文搞懂在 HTTP 如何 one-api 调用,实操指南来袭!
- 探索海洋数据的宝库:Amentum海洋数据探测API的潜力
- 云原生 API 网关 APISIX 入门教程
- Deribit API – 入门指南
- API Key 密钥:深入理解与应用
- 2025年国内免费生成式 AI API 平台大盘点:探索国产大模型的开放能力
- 使用DeepSeek和Claude绘制出高质量的SVG 图片
- Kimi K2 API 调用全指南:解锁国产大模型的强大能力
- Amazon的API描述语言Smithy概述
- 向日葵开放平台:如何让远程办公和支持变得轻而易举?
- 常见的api认证方式:应用场景与优势