Project Panama
https://openjdk.org/projects/panama/ https://docs.oracle.com/en/java/javase/19/core/foreign-function-and-memory-api.html#GUID-FBE990DA-C356-46E8-9109-C75567849BA8
Project Panama 是 OpenJDK 的一个项目,旨在改善 Java 与本地代码(native code)之间的互操作性。 简化 Java 与本地库的交互,同时提高性能。Project Panama 引入了许多新的概念和对象
正式发布于 JDK 22 : JEP 454: Foreign Function & Memory API
The Foreign Function & Memory API (FFM API) defines classes and interfaces so that client code in libraries and applications can
- Control the allocation and deallocation of foreign memory
(MemorySegment,Arena, andSegmentAllocator), - Manipulate and access structured foreign memory
(MemoryLayoutandVarHandle), and - Call foreign functions (
Linker,SymbolLookup,FunctionDescriptor, andMethodHandle).
The FFM API resides in the java.lang.foreign package of the java.base module.
As a brief example of using the FFM API, here is Java code that obtains a method handle for a C library function radixsort and then uses it to sort four strings which start life in a Java array (a few details are elided).
// 1. Find foreign function on the C library path
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle radixsort = linker.downcallHandle(stdlib.find("radixsort"), ...);
// 2. Allocate on-heap memory to store four strings
String[] javaStrings = { "mouse", "cat", "dog", "car" };
// 3. Use try-with-resources to manage the lifetime of off-heap memory
try (Arena offHeap = Arena.ofConfined()) {
// 4. Allocate a region of off-heap memory to store four pointers
MemorySegment pointers
= offHeap.allocate(ValueLayout.ADDRESS, javaStrings.length);
// 5. Copy the strings from on-heap to off-heap
for (int i = 0; i < javaStrings.length; i++) {
MemorySegment cString = offHeap.allocateFrom(javaStrings[i]);
pointers.setAtIndex(ValueLayout.ADDRESS, i, cString);
}
// 6. Sort the off-heap data by calling the foreign function
radixsort.invoke(pointers, javaStrings.length, MemorySegment.NULL, '\0');
// 7. Copy the (reordered) strings from off-heap to on-heap
for (int i = 0; i < javaStrings.length; i++) {
MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i);
javaStrings[i] = cString.reinterpret(...).getString(0);
}
} // 8. All off-heap memory is deallocated here
assert Arrays.equals(javaStrings,
new String[] {"car", "cat", "dog", "mouse"}); // true关键概念
Project Panama 核心概念
一、核心 API:Foreign Function & Memory (FFM)
| 概念 | 对象/方法 | 作用 | 示例代码片段 |
|---|---|---|---|
| 本地内存管理 | MemorySegment | 表示一块本地内存区域,支持自动资源管理(通过 Arena)。 | MemorySegment segment = MemorySegment.allocateNative(100, arena); |
| 内存布局描述 | MemoryLayout | 描述内存结构(如结构体、数组),用于类型安全的内存访问。 | MemoryLayout structLayout = MemoryLayout.structLayout(JAVA_INT, JAVA_INT); |
| 内存生命周期管理 | Arena | 管理 MemorySegment 的生命周期(自动释放或手动作用域控制)。 | try (Arena arena = Arena.ofConfined()) { ... } |
| 本地函数链接器 | Linker | 绑定 Java 与本地函数,支持跨平台调用。 | Linker linker = Linker.nativeLinker(); |
| 符号查找 | SymbolLookup | 查找本地库中的函数或全局变量。 | SymbolLookup stdlib = linker.defaultLookup();SymbolLookup lookup = SymbolLookup.libraryLookup("D:/my.dll", tempArena) |
| 函数描述符 | FunctionDescriptor | 定义本地函数的签名(参数类型 + 返回类型)。 | FunctionDescriptor funcDesc = FunctionDescriptor.of(JAVA_INT, ADDRESS); |
| 本地函数调用 | Linker.downcallHandle() | 将本地函数绑定为 Java 可调用的 MethodHandle。 | MethodHandle handle = linker.downcallHandle(symbol, funcDesc); |
| 回调函数指针生成 | Linker.upcallStub() | 将 Java 方法转换为本地函数指针(供 C 调用)。 | MemorySegment callbackPtr = linker.upcallStub(handle, desc, arena); |
| 结构化内存访问 | VarHandle | 类型安全地读写 MemorySegment 中的字段。 | VarHandle xHandle = structLayout.varHandle(PathElement.groupElement("x")); |
| 类型化内存访问 | ValueLayout | 定义基本数据类型的内存布局(如 JAVA_INT, ADDRESS)。 | ValueLayout intLayout = ValueLayout.JAVA_INT; |
| 内存地址操作 | MemoryAddress | 表示指向内存的地址(通常由 MemorySegment 管理)。 | MemoryAddress address = segment.address(); |
| 堆内存分配 | SegmentAllocator | 分配堆外内存的通用接口(Arena 实现了此接口)。 | SegmentAllocator allocator = Arena.global(); |
| 异常处理 | UnsupportedOperationException | 当操作违反内存安全或平台限制时抛出(如越界访问)。 | try { segment.get(JAVA_INT, 100); } catch (Exception e) { ... } |
| 向量化操作 | Vector | 支持 SIMD 指令的向量计算(与 Panama 结合优化本地代码性能)。 | IntVector vector = IntVector.fromArray(IntVector.SPECIES_256, array, 0); |
二、内存管理
1. 内存分配
// 在 Arena 中分配堆外内存(自动释放)
try (Arena arena = Arena.ofConfined()) {
MemorySegment buffer = arena.allocate(1024); // 分配 1KB
buffer.set(ValueLayout.JAVA_INT, 0, 42); // 写入 int 值
}2. 结构化内存访问
// 定义结构体布局
final MemoryLayout LAYOUT = MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("x"),
ValueLayout.JAVA_INT.withName("y")
);
// 创建 VarHandle 访问字段
VarHandle xHandle = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("x"));
VarHandle yHandle = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("y"));
// 写入/读取结构体
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(LAYOUT);
//设置 这里的0L是相对偏移量, 大部分都是0?
xHandle.set(segment, 0L, 10); // 等价于 segment.set(ValueLayout.JAVA_INT, 0, 10)
yHandle.set(segment, 0L, 20); // 等价于 segment.set(ValueLayout.JAVA_INT, 4, 20)
//读取 by VarHandle
Object xx = xHandle.get(segment, 0);//
Object yy = yHandle.get(segment, 0);
System.out.println(" xx = "+ xx +", yy = "+yy);
//读取 by MemorySegment
int x = segment.get(ValueLayout.JAVA_INT, 0);// 0 是偏移值
int y = segment.get(ValueLayout.JAVA_INT, 4);// 4 是偏移值
System.out.println(" x = "+ x +", y = "+y);// x = 10, y = 20
}基础类型对照表
| Java ValueLayout | C/C++ 类型 | 大小 (Bytes) | 对齐要求 | 典型使用场景 |
|---|---|---|---|---|
JAVA_BYTE | int8_t / char | 1 | 1 | 处理二进制数据、字节流 |
JAVA_SHORT | int16_t / short | 2 | 2 | 短整数、Unicode 字符 (UTF-16) |
JAVA_INT | int32_t / int | 4 | 4 | 常规整数、错误代码 |
JAVA_LONG | int64_t / long | 8 | 8 | 大整数、时间戳 |
JAVA_FLOAT | float | 4 | 4 | 单精度浮点数 |
JAVA_DOUBLE | double | 8 | 8 | 双精度浮点数 |
JAVA_CHAR | wchar_t (C++11+) | 2 (Win) /4 (Unix) | 2/4 | 宽字符处理 (如Windows API) |
JAVA_BOOLEAN | bool (C++) | 1 | 1 | 布尔标志位 |
ADDRESS | void* / char* | 4 (32位) /8 (64位) | 按平台 | 指针、字符串、回调函数指针 |
关于 const char * 的访问
返回的const char*指向的是静态内存区域,而Java的MemorySegment默认不知道这个区域的长度,导致在尝试读取时无法确定边界;
就 ** 的离谱!!
MemorySegment result = (MemorySegment) getenv.invoke(varName);
//重新划定 MemorySegment 内存的大小
MemorySegment validSegment = result.reinterpret(Long.MAX_VALUE, arena, null);
// 读取
private static String readCString(MemorySegment cStr) {
StringBuilder str = new StringBuilder();
long length = 0;
byte b;
while ((b = cStr.get(ValueLayout.JAVA_BYTE, length)) != 0) {
str.append((char) b);
length++;
}
return str.toString();
}嵌套的结构体
C 定义
typedef struct {
int idisable; /* 0 = "support this feature", 1 = "not support" */
int imin; /* minimum value */
int imax; /* maximum value */
int idef; /* default value */
} ToupnamRange;
typedef struct {
char id[64]; /* unique camera id, used for Toupnam_Open */
char sn[64]; /* serial number */
char name[64];
char model[64];
char version[64];
char addr[64]; /* ip */
char url[256]; /* playback url, such as rtsp://xxxx/yyyy */
unsigned state; /* TOUPNAM_STATE_xxx */
unsigned flag; /* TOUPNAM_FLAG_xxx */
ToupnamRange range[64];
} ToupnamDevice;
/* enumerate the cameras discovered by the computer, return the number
sz: size of the array
when sz is too small, return value will be greater than sz, means that the caller must use bigger array
*/
TOUPNAM_API(unsigned) Toupnam_Enum(ToupnamDevice arr[], unsigned sz);FFI 定义
ToupnamRange.java
public class ToupnamRange {
public static final GroupLayout LAYOUT = MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("idisable"),
ValueLayout.JAVA_INT.withName("imin"),
ValueLayout.JAVA_INT.withName("imax"),
ValueLayout.JAVA_INT.withName("idef")
);
private static final VarHandle
IDISABLE = LAYOUT.varHandle(PathElement.groupElement("idisable")),
IMIN = LAYOUT.varHandle(PathElement.groupElement("imin")),
IMAX = LAYOUT.varHandle(PathElement.groupElement("imax")),
IDEF = LAYOUT.varHandle(PathElement.groupElement("idef"));
private final MemorySegment segment;
public ToupnamRange(MemorySegment segment) {
this.segment = segment;
}
public boolean isSupported() {
return (int) IDISABLE.get(segment, 0L) == 0;
}
public int min() {
return (int) IMIN.get(segment, 0L);
}
public int max() {
return (int) IMAX.get(segment, 0L);
}
public int defaultValue() {
return (int) IDEF.get(segment, 0L);
}
}ToupnamDevice.java
public class ToupnamDevice {
private static final int
STRING_LEN_64 = 64,
STRING_LEN_256 = 256,
TOUPNAM_MAX = 64;
// 定义结构体布局
public static final GroupLayout LAYOUT = MemoryLayout.structLayout(
MemoryLayout.sequenceLayout(STRING_LEN_64, ValueLayout.JAVA_BYTE).withName("id"),
MemoryLayout.sequenceLayout(STRING_LEN_64, ValueLayout.JAVA_BYTE).withName("sn"),
MemoryLayout.sequenceLayout(STRING_LEN_64, ValueLayout.JAVA_BYTE).withName("name"),
MemoryLayout.sequenceLayout(STRING_LEN_64, ValueLayout.JAVA_BYTE).withName("model"),
MemoryLayout.sequenceLayout(STRING_LEN_64, ValueLayout.JAVA_BYTE).withName("version"),
MemoryLayout.sequenceLayout(STRING_LEN_64, ValueLayout.JAVA_BYTE).withName("addr"),
MemoryLayout.sequenceLayout(STRING_LEN_256, ValueLayout.JAVA_BYTE).withName("url"),
ValueLayout.JAVA_INT.withName("state"),
ValueLayout.JAVA_INT.withName("flag"),
MemoryLayout.sequenceLayout(TOUPNAM_MAX, ToupnamRange.LAYOUT).withName("range")
);
// 仅对值布局字段使用 VarHandle
private static final VarHandle
STATE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("state")),
FLAG = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("flag"));
// 字符串字段通过偏移量访问
private static final long
ID_OFFSET = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("id")),
SN_OFFSET = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("sn")),
NAME_OFFSET = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("name")),
MODEL_OFFSET = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("model")),
VERSION_OFFSET = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("version")),
ADDR_OFFSET = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("addr")),
URL_OFFSET = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("url"));
private final MemorySegment segment;
public ToupnamDevice(MemorySegment segment) {
this.segment = segment;
}
///////////////////////////////////////////
// 字段访问方法
///////////////////////////////////////////
/* 字符串字段 */
public String id() {
return getString(ID_OFFSET);
}
public String sn() {
return getString(SN_OFFSET);
}
... 省略 ...
/* 整型字段 */
public int state() {
return (int) STATE.get(segment, 0L);
}
public int flag() {
return (int) FLAG.get(segment, 0L);
}
///////////////////////////////////////////
// 辅助方法
///////////////////////////////////////////
private String getString(long offset) {
return segment.asSlice(offset).getString(offset);
}
private void setString(long offset, String value, int maxLength) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
int length = Math.min(bytes.length, maxLength - 1);
MemorySegment.copy(
MemorySegment.ofArray(bytes), 0,
segment.asSlice(offset), 0, length
);
segment.set(ValueLayout.JAVA_BYTE, offset + length, (byte)0);
}
/* 嵌套结构体数组 */
public ToupnamRange[] ranges() {
long rangeOffset = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("range"));
long elementSize = ToupnamRange.LAYOUT.byteSize();
ToupnamRange[] ranges = new ToupnamRange[TOUPNAM_MAX];
for (int i = 0; i < TOUPNAM_MAX; i++) {
ranges[i] = new ToupnamRange(
segment.asSlice(rangeOffset + i * elementSize, elementSize)
);
}
return ranges;
}
}FFI 数组结构体 创建和访问
try (Arena arena = Arena.ofConfined()) {
// 1. 分配包含3个结构体的数组内存
// 类似 cpp 的 ToupnamDevice[3]
MemorySegment devicesMem = arena.allocate(
MemoryLayout.sequenceLayout(3, ToupnamDevice.layout())
);
// 2. 计算单个元素字节大小
long elementSize = ToupnamDevice.layout().byteSize();
// 3. 遍历访问每个元素
for (int i = 0; i < 3; i++) {
// 计算当前元素偏移量
long offset = i * elementSize;
// 4. 获取当前元素的内存段
MemorySegment currentElement = devicesMem.asSlice(offset, elementSize);
// 5. 包装成ToupnamDevice对象
ToupnamDevice device = new ToupnamDevice(currentElement);
// 6. 访问具体字段
System.out.println("Device " + i + " ID: " + device.id());
System.out.println("Device " + i + " IP: " + device.addr());
// 7. 访问嵌套的range数组(示例访问第一个range)
ToupnamRange firstRange = device.range()[0];
System.out.printf("Range 0: %d-%d (default: %d)\n",
firstRange.imin(), firstRange.imax(), firstRange.idef());
}
}实例 调用一个工业相机SDK
声明
/*
when toupnam.dll/libtoupnam.so discovery new camera, pCallback will be called.
pCallback can be NULL if the application does not interest this.
Toupnam_Init: call only once when application startup
Toupnam_Fini: call only once when application exit
*/
TOUPNAM_API(void) Toupnam_Init(PTOUPNAM_EVENT_CALLBACK pCallback, void* pCallbackCtx);
/* enumerate the cameras discovered by the computer, return the number
sz: size of the array
when sz is too small, return value will be greater than sz, means that the caller must use bigger array
*/
TOUPNAM_API(unsigned) Toupnam_Enum(ToupnamDevice arr[], unsigned sz);
对应 FFI
// 初始化函数
public static void init(ToupnamInit.ToupnamEventListener listener) {
try (Arena arena = Arena.ofConfined()) {
// 创建回调代理
MethodHandle proxy = MethodHandles.lookup().findVirtual(
ToupnamInit.CallbackProxy.class, "invoke",
MethodType.methodType(void.class, int.class, int.class, MemorySegment.class, MemorySegment.class)
);
// 分配回调内存 upcal
MemorySegment callback = LINKER.upcallStub(
proxy.bindTo(new ToupnamInit.CallbackProxy(listener)),
FunctionDescriptor.ofVoid(
ValueLayout.JAVA_INT,
ValueLayout.JAVA_INT,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS
),
arena
);
SymbolLookup lookup = SymbolLookup.libraryLookup(UVCHAM_DLL, arena);
// 获取初始化函数句柄 downcall
MethodHandle initHandle = LINKER.downcallHandle(
lookup.find("Toupnam_Init").orElseThrow(),
FunctionDescriptor.ofVoid(
ValueLayout.ADDRESS,
ValueLayout.ADDRESS
)
);
// 调用初始化函数
initHandle.invokeExact(callback, MemorySegment.NULL);
List<ToupnamDevice> toupnamDevices = ToupnamCamera.enumerateDevices(arena);
if (toupnamDevices.isEmpty() ){
System.out.println("not devices found ");
}
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException("Init failed", e);
}
}
public static List<ToupnamDevice> enumerateDevices(Arena tempArena) {
List<ToupnamDevice> devices = new ArrayList<>();
try{
int MAX = 3;
// 分配设备数组内存
MemorySegment devicesMem = tempArena.allocate(
MemoryLayout.sequenceLayout(MAX, ToupnamDevice.LAYOUT)
);
// 获取枚举函数
SymbolLookup lookup = SymbolLookup.libraryLookup(UVCHAM_DLL, tempArena);
// 获取函数句柄
MethodHandle enumHandle = LINKER.downcallHandle(
lookup.find("Toupnam_Enum").orElseThrow(),
FunctionDescriptor.of(
ValueLayout.JAVA_INT,
ValueLayout.ADDRESS,
ValueLayout.JAVA_INT
)
);
// 调用枚举函数
int count = (int) enumHandle.invoke(devicesMem, MAX);
log.info(" find count = {} ", count);
// 解析设备信息
count = Math.min(count, MAX);
for (int i = 0; i < count; i++) {
MemorySegment deviceMem = devicesMem.asSlice(i * ToupnamDevice.LAYOUT.byteSize());
ToupnamDevice device = new ToupnamDevice(deviceMem);
devices.add(device);
}
System.out.println("toupnamDevices = "+ devices);
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException("Device enumeration failed", e);
}
return devices;
}代码归档
Transclude of ToupnamCamera.zip
三、本地函数调用
1. Downcall(Java → C)
// 查找本地函数符号
SymbolLookup stdlib = linker.defaultLookup();
MemorySegment printfSymbol = stdlib.find("printf").orElseThrow();
// 定义函数描述符
FunctionDescriptor printfDesc = FunctionDescriptor.of(
ValueLayout.JAVA_INT, // 返回值类型(int)
ValueLayout.ADDRESS // 参数类型(char*)
);
// 绑定并调用
MethodHandle printfHandle = linker.downcallHandle(printfSymbol, printfDesc);
try (Arena arena = Arena.ofConfined()) {
MemorySegment msg = arena.allocateUtf8String("Hello from Java!\n");
printfHandle.invoke(msg); // 输出字符串
}
// 对于 返回值为void的函数
MemorySegment callback = LINKER.upcallStub(
FunctionDescriptor.ofVoid(
ValueLayout.JAVA_INT,
ValueLayout.JAVA_INT,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS
),
arena
);
函数指针 回调
函数原型
typedef struct {
int result;
unsigned length;
void* ptr;
void* ctx;
} ToupnamEventExtra;
// PTOUPNAM_EVENT_CALLBACK 函数指针的定义
typedef void (__stdcall* PTOUPNAM_EVENT_CALLBACK)(unsigned nEvent, unsigned nPara, void* pCallbackCtx, ToupnamEventExtra* pExtra);
/*
HToupnam 是相机句柄
截图并回调 pCaptureCallback
pstrFileName:
NULL -> capture image and then return by callback
"raw" -> capture raw image and then return by callback
"abc.jpg" -> capture image and then save it in the camera sd card with filename 'abc.jpg'
"abc.raw" -> capture raw image and then save it in the camera sd card with filename 'abc.raw'
"thumbnail" -> capture the thumbnail image and then return by callback
"*" -> capture image and then save it in the camera sd card with auto generated file name
"*.raw" -> capture raw image and then save it in the camera sd card with auto generated file name
*/
TOUPNAM_API(HRESULT) Toupnam_Capture(HToupnam h, const char* pstrFileName, PTOUPNAM_CAPTURE_CALLBACK pCaptureCallback, void* pCallbackCtx);对应 Java FFI
// Capture java 回调接口
public interface CaptureCallback {
void onCapture(int result, MemorySegment pData, long nLength, MemorySegment imageData, MemorySegment ctx);
}
public synchronized void Capture() {
final CaptureCallback proxyObj = new CaptureCallback() {
@Override
public void onCapture(int result, MemorySegment pData, long nLength, MemorySegment imageData, MemorySegment ctx) {
//打印全部参数
log.info("Capture fileName={}, result = {}, pData={}, nLength={}, imageData={}, ctx={}"
,fileName, result, pData, nLength, imageData, ctx);
}
};
//java 回调对象的描述
MethodHandle proxy = MethodHandles.lookup().findVirtual(
CaptureCallback.class, "onCapture",
MethodType.methodType(void.class, int.class, MemorySegment.class, long.class,
MemorySegment.class, MemorySegment.class)
);
// 分配 PTOUPNAM_CAPTURE_CALLBACK 的本地内存, 绑定到Java 供C/C++调用;
MemorySegment callback = LINKER.upcallStub(
proxy.bindTo(proxyObj),//绑定 java 回调对象
FunctionDescriptor.ofVoid(
ValueLayout.JAVA_INT,
ValueLayout.ADDRESS,
ValueLayout.JAVA_LONG,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS
),
arena
);
//查找 DLL 的 Toupnam_Capture 函数指针
MethodHandle Toupnam_Capture =LINKER.downcallHandle(
lookup.find("Toupnam_Capture").orElseThrow(),
FunctionDescriptor.ofVoid(
ValueLayout.ADDRESS, // HToupnam h
ValueLayout.ADDRESS, // const char* pstrFileName
ValueLayout.ADDRESS, // PTOUPNAM_CAPTURE_CALLBACK
ValueLayout.ADDRESS // void* pCallbackCtx
)
);
//调用
Toupnam_Capture.invokeExact(DEVICE_HWND, MemorySegment.NULL, callback, MemorySegment.NULL);
Thread.sleep(500);
}
2. Upcall 传递函数指针(C → Java函数)
// 定义 Java 回调函数
interface Callback {
int add(int a, int b);
}
Callback adder = (a, b) -> a + b;
// 生成函数指针
MethodHandle adderHandle = MethodHandles.lookup()
.findVirtual(Callback.class, "add", MethodType.methodType(int.class, int.class, int.class));
MemorySegment adderPtr = linker.upcallStub(
adderHandle.bindTo(adder),
FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT),
Arena.global()
);示例
编译 DLL
一个简单的 DLL,导出一个加法函数 int add(int a, int b)
#include <stdio.h>
// 确保使用 extern "C" 避免 C++ 名称修饰
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) int add(int a, int b) {
return a + b;
}
#ifdef __cplusplus
}
#endif调用 DLL
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
public static void main(String[] args) {
// 1. 自动内存管理
try (Arena arena = Arena.ofConfined()) {
// 2. 加载 DLL Linker linker = Linker.nativeLinker();
SymbolLookup lookup = SymbolLookup.libraryLookup("math.dll", (Arena) arena.scope());
// 3. 查找函数符号
MemorySegment addFunc = lookup.find("add").orElseThrow();
// 4. 定义函数描述符(参数和返回值类型)
FunctionDescriptor descriptor = FunctionDescriptor.of(
ValueLayout.JAVA_INT, // 返回值类型
ValueLayout.JAVA_INT, // 参数1
ValueLayout.JAVA_INT // 参数2
);
// 5. 绑定方法句柄
MethodHandle addHandle = linker.downcallHandle(addFunc, descriptor);
// 6. 调用函数
int result = (int) addHandle.invoke(3, 5);
System.out.println("Result: " + result); // 输出 8
}
}