MemorySegment 与 Arena
分配堆外内存
MemorySegment 与 Arena 是 CoddyKit 上的免费 Java Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
堆外内存
FFM 使用两种核心类型来表示本地堆外内存:MemorySegment 表示一段连续的内存,Arena 控制其生命周期。
二者结合后,可以替代过去不安全的手动缓冲区管理方式。
什么是 MemorySegment
MemorySegment 是内存块上的带类型视图。它知道自己的大小和生命周期,每次访问都会进行边界检查。
内存段可以位于堆外(本地内存),也可以包装堆上的数组。
什么是 Arena
Arena 会分配内存段,并定义这些内存段何时被释放。当 Arena 关闭时,它拥有的所有内存段都会在一个确定的步骤中失效。
Arena.ofConfined():单线程使用,显式关闭Arena.ofShared():支持多线程访问Arena.global():永不释放Arena.ofAuto():由 GC 释放
分配内存段
在资源自动关闭的 try 语句中使用受限 Arena,是最常见的模式。分配字节、使用它们,关闭时即可释放全部资源。
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment seg = arena.allocate(16);
System.out.println("Allocated bytes: " + seg.byteSize());
}
System.out.println("Arena closed, memory freed");
}
}读写
您可以通过带类型的访问器进行读写;这些访问器接收一个 ValueLayout 和一个偏移量。这样可以确保访问具有类型安全性,并满足对齐要求。
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment seg = arena.allocate(ValueLayout.JAVA_INT);
seg.set(ValueLayout.JAVA_INT, 0, 42);
int value = seg.get(ValueLayout.JAVA_INT, 0);
System.out.println("Stored and read: " + value);
}
}
}分配带类型的存储槽
不要按原始字节数分配,而应根据布局进行分配。arena.allocate(JAVA_INT) 会为一个整数值准确预留足够且正确对齐的字节。
这样可以避免手动计算大小。
值数组
要存储多个元素,可以分配一个序列布局,或传入元素数量。按索引访问时,会根据元素大小计算偏移量。
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment ints = arena.allocate(ValueLayout.JAVA_INT, 5);
for (int i = 0; i < 5; i++) {
ints.setAtIndex(ValueLayout.JAVA_INT, i, i * 10);
}
System.out.println("Element 3: " + ints.getAtIndex(ValueLayout.JAVA_INT, 3));
}
}
}边界检查
在内存段范围之外进行读写会抛出 IndexOutOfBoundsException。在其 Arena 关闭后访问内存段会抛出 IllegalStateException。
正是这些检查让 FFM 的内存访问具备安全性,而 JNI 不具备这种安全性。
隔离
受限 Arena 会将其内存段绑定到创建它们的线程。其他线程访问这些内存段时会抛出 WrongThreadException。
对于确实需要共享的内存,请使用 Arena.ofShared()。它允许多线程访问,但会产生少量开销。
处理字符串
FFM 提供了在 Java 字符串和 C 字符串之间进行转换的辅助方法。allocateUtf8String(较新的方法是 allocateFrom)会写入以空字符结尾的字符串,而 getUtf8String 会将其读取回来。
import java.lang.foreign.*;
public class Main {
public static void main(String[] args) {
try (Arena arena = Arena.ofConfined()) {
MemorySegment cString = arena.allocateUtf8String("hello");
System.out.println("Read back: " + cString.getUtf8String(0));
}
}
}选择 Arena
指导原则:
- 短生命周期、单线程使用的缓冲区:
ofConfined() - 在线程之间共享:
ofShared() - 由 GC 管理生命周期:
ofAuto() - 进程范围内的常量:
global()
快速检查
回想一下,什么决定了本地内存何时被释放。
总结
您已经了解了堆外内存管理:
MemorySegment:经过边界检查的内存区域Arena:控制生命周期(受限、共享、自动、全局)- 通过
ValueLayout使用get/set和getAtIndex进行带类型的访问 - 通过边界检查和隔离提供安全性
下一步:使用下调用句柄调用本地函数。
常见问题解答
「MemorySegment 与 Arena」课时是免费的吗?
是的 — 「MemorySegment 与 Arena」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「MemorySegment 与 Arena」这节课中我会学到什么?
分配堆外内存 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「MemorySegment 与 Arena」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 为什么选择 FFM 而不是 JNI
- MemorySegment 与 Arena
- 下调用句柄
- 布局与结构体