FFM API是现代Java调用C库的推荐方式,它通过Linker和SymbolLookup动态查找函数符号,用FunctionDescriptor声明签名,以MemorySegment封装数据并由Arena自动管理内存生命周期,兼顾类型安全、性能与简洁性。

Java外部函数与内存API(FFM API)是现代Java调用C库的推荐方式,它比JNI更安全、简洁,且性能更好。核心在于类型安全的函数绑定、显式内存生命周期管理,以及避免手动指针操作。
加载并定位C函数
不再依赖System.loadLibrary字符串匹配,而是通过Linker和SymbolLookup动态查找符号:
- 使用Linker.nativeLinker()获取系统原生链接器
- 调用linker.defaultLookup()访问标准C库(如libc),或用SymbolLookup.libraryLookup("mylib", Path.of("/path"))加载自定义so/dll
- 用find("function_name")获取MemorySegment形式的函数地址,返回Optional,需判空处理
声明函数签名并构造调用句柄
FunctionDescriptor描述参数与返回值布局,必须严格匹配C函数原型:
- 基本类型用ValueLayout.JAVA_INT、JAVA_LONG等;指针用ValueLayout.ADDRESS
- 多个参数按C调用约定顺序排列,例如strlen只接受一个const char* → FunctionDescriptor.of(JAVA_LONG, ADDRESS)
- 调用linker.downcallHandle(functionSegment, descriptor)生成MethodHandle,支持invokeExact强类型调用
安全传递和管理内存数据
所有传给C函数的数据必须封装为MemorySegment,禁止直接传Java对象或数组:
立即学习“Java免费学习笔记(深入)”;
- 字符串:用MemorySegment.ofArray("hello".getBytes(StandardCharsets.UTF_8)),或配合Addressable接口
- 结构体:用MemoryLayout.structLayout()定义字段偏移,再allocateNative()分配对应大小内存
- 务必用try-with-resources包裹MemorySegment,确保close()自动释放堆外内存;若需长期持有,用Arena.openConfined()或Arena.openShared()
处理异常与线程安全
FFM API本身不抛出Checked Exception,但错误会以RuntimeException形式暴露:
- 函数地址为空、布局不匹配、内存越界访问都会触发IllegalStateException或IllegalArgumentException
- 默认Arena(如global或confined)已适配虚拟线程,无需额外同步;但共享Arena需注意多线程写入竞争
- 调试时可启用-Dforeign.restricted=allow,并结合jcmd PID VM.native_memory查看本地内存使用情况


















