外部函数与内存API - Java 22 - 未记录
Foreign Function and Memory API(外部函数与内存API)是 Panama 项目的一部分,该项目旨在简化 Java 与外部(非 Java)API 的交互,例如用 C、C++ 或汇编语言编写的本地代码。由于许多原生库并非用 Java 编写,因此需要这种功能。以下是一些常见的原生库示例:
- OpenGL(图形处理)
- TensorFlow 和 ONNX(机器学习框架)
- OpenSSL(安全通信)
- CUDA(GPU 通用计算)
过去,Java 使用 JNI(Java Native Interface) 来实现与这些库的交互。JNI 允许 Java 类定义本机方法,这些方法的实现由 C 或 C++ 等本地语言编写。然而,这种方法存在以下缺点:
- 跨平台问题:Java 的“一次编写,到处运行”特性被破坏。支持多个平台(如 Windows、MacOS 和 Linux)需要为每个平台和架构单独构建库。
- 维护成本高:构建、维护和部署这些库的成本显著增加。
- 通信开销:Java 和本地代码之间的数据交换需要进行双向转换,这可能导致性能瓶颈。
为了解决这些问题,Panama 项目引入了多种工具和 API,包括:
- Foreign Function and Memory API:用于分配和访问堆外内存,并直接从 Java 调用外部函数。
- 矢量 API:在 Java 中执行矢量计算,提升性能。
- JExtract:自动生成 Java 与本地代码绑定的工具。
本文将重点介绍 Foreign Function and Memory API(简称 FFM API)。
外部存储器访问
在 Java 中,通过 new 关键字创建的对象存储在 JVM 的堆内存中,由垃圾回收器管理。然而,垃圾回收可能带来不可预测的性能开销,因此许多性能关键的库更倾向于使用堆外内存,由本地语言自行管理分配和释放。
Java 过去通过 ByteBuffer API 和 sun.misc.Unsafe 提供堆外内存访问,但它们各有局限性:
- ByteBuffer API:内存区域大小限制为 2GB,且内存释放由垃圾回收器控制,而非开发者。
- sun.misc.Unsafe:提供了过于细粒度的控制,容易导致悬空指针等错误。
FFM API 提供了一种更平衡的方式来访问堆外内存,同时保证安全性和灵活性。

外部内存的分配与释放
内存段
内存段是连续内存块的抽象表示,可以位于堆外或堆上。通过定义内存地址范围(空间边界)和生命周期(时间边界),内存段确保了内存访问的安全性。内存段分为以下几种类型:
- 本机段:分配在堆外内存中(类似于
malloc)。 - 映射段:使用映射文件的堆外内存区域(类似于
mmap)。 - 堆上段:基于 Java 数组或字节缓冲区的内存段。
竞技场
竞技场(Arena)定义了它分配的内存段的边界。例如:
MemorySegment segment = Arena.global().allocate(100);
上述代码分配了 100 字节的内存,地址范围为 b 到 b+99,其中 b 是基址。竞技场有多种类型,具体如下表所示:

FFM API 提供了 SegmentAllocator 接口,用于抽象内存段的分配和初始化。Arena 类实现了该接口,支持分配本机内存段。
操纵和访问结构化外部存储器
内存布局
MemoryLayout 用于声明性地描述内存段的内容。以下是几种常见的内存布局:
- ValueLayout:用于建模基本数据类型,定义了大小、对齐方式和字节顺序等。
- SequenceLayout:表示元素布局的重复序列。
- GroupLayout:表示多个不同成员布局的组合,分为:
- 结构布局:成员按顺序排列。
- 联合布局:成员共享相同的起始偏移量。
- PaddingLayout:用于对齐成员布局,内容可忽略。
调用外部函数
符号查找
调用外部函数的第一步是找到目标函数或全局变量的地址。FFM API 提供了 SymbolLookup 接口,用于在本地库中查找符号。例如:
SymbolLookup lookup = SymbolLookup.libraryLookup(Paths.get(pathToLibrary), arena);
MemorySegment testSymbol = lookup.find("test")
.orElseThrow(() -> new RuntimeException("找不到符号 'test'"));
FFM API 提供了以下三种 SymbolLookup 对象:
libraryLookup(String, Arena):定位用户指定库中的符号。loaderLookup():查找当前类加载器加载的本地库中的符号。defaultLookup():查找常用本地库中的符号。
链接器
Linker 接口用于实现 Java 与本地代码的互操作,支持下调用(调用本地代码)和上调用(本地代码调用 Java)。通过 Linker.nativeLinker(),可以获取与当前平台关联的链接器。
下调用
通过 MethodHandle.downcallHandle,可以将外部函数的地址与 Java 方法句柄绑定,从而在 Java 中调用本地函数。例如:
MethodHandle addFunctionHandle = linker.downcallHandle(addSymbol, addDescriptor);
上调用
通过 MemorySegment.upcallStub,可以将 Java 方法转换为函数指针,供本地代码调用。
函数描述符
FunctionDescriptor 用于描述外部函数的签名,包括参数类型和返回类型。例如:
FunctionDescriptor descriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT);
示例:调用 C 函数
以下是一个完整示例,展示如何通过 FFM API 调用用 C 编写的函数。
C 程序
创建一个名为 addition.c 的文件,内容如下:
#include
int add(int a, int b) {
return a + b;
}
编译为共享库(以 macOS 为例):
gcc -shared -o addition.dylib -fPIC addition.c
Java 程序
以下是 Java 程序代码:
import java.lang.foreign.*;
import java.lang.invoke.*;
import java.nio.file.*;
public class MemoryAccessExample {
public static void main(String[] args) throws Throwable {
FunctionDescriptor addDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT);
String libraryPath = System.getProperty("user.home") + "/Downloads/addition.dylib";
try (Arena arena = Arena.ofConfined()) {
SymbolLookup lookup = SymbolLookup.libraryLookup(Paths.get(libraryPath), arena);
MemorySegment addSymbol = lookup.find("add")
.orElseThrow(() -> new RuntimeException("找不到函数 'add'"));
Linker linker = Linker.nativeLinker();
MethodHandle addFunctionHandle = linker.downcallHandle(addSymbol, addDescriptor);
int num1 = 25;
int num2 = 10;
try (Arena offHeap = Arena.ofConfined()) {
MemorySegment intMemory = offHeap.allocate(ValueLayout.JAVA_INT, 2);
intMemory.set(ValueLayout.JAVA_INT, 0, num1);
intMemory.set(ValueLayout.JAVA_INT, 4, num2);
int result = (int) addFunctionHandle.invoke(num1, num2);
System.out.println("总和为:" + result);
}
}
}
}
运行程序时需启用预览功能:
java --enable-preview MemoryAccessExample
输出结果为:
总和为:35
使用 FFM 的优势
FFM API 提供了一种安全、简洁且纯 Java 的方式来替代 JNI 和 sun.misc.Unsafe,同时保证了与它们相当的性能。通过统一的 API,开发者可以避免 JNI 的复杂性和潜在漏洞,从而更高效地与本地代码交互。
如需了解更多信息,请参考官方文档。
原文链接: https://www.unlogged.io/post/foreign-function-and-memory-api---java-22
热门API
- 1. AI文本生成
- 2. AI图片生成_文生图
- 3. AI图片生成_图生图
- 4. AI图像编辑
- 5. AI视频生成_文生视频
- 6. AI视频生成_图生视频
- 7. AI语音合成_文生语音
- 8. AI文本生成(中国)