0Pricing
Java Academy · 课时

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 为什么选择 FFM 而不是 JNI
  2. MemorySegment 与 Arena
  3. 下调用句柄
  4. 布局与结构体
← 返回 Java Academy