Language 高级

FFM 让 Java 直接调用 C 库,无需 JNI 样板代码或在 C 端了解 Java。

✕ Java 1.1+
public class CallCFromJava {
    static { System.loadLibrary("strlen-jni"); }
    public static native long strlen(String s);
    public static void main(String[] args) {
        long ret = strlen("Bambi");
        System.out.println("Return value " + ret); // 5
    }
}

// Run javac -h to generate the .h file, then write C:
// #include "CallCFromJava.h"
// #include <string.h>
// JNIEXPORT jlong JNICALL Java_CallCFromJava_strlen(
//     JNIEnv *env, jclass clazz, jstring str) {
//     const char* s = (*env)->GetStringUTFChars(env, str, NULL);
//     jlong len = (jlong) strlen(s);
//     (*env)->ReleaseStringUTFChars(env, str, s);
//     return len;
// }
✓ Java 22+
void main() throws Throwable {
    try (var arena = Arena.ofConfined()) {
        // Use any system library directly — no C wrapper needed
        var stdlib = Linker.nativeLinker().defaultLookup();
        var foreignFuncAddr = stdlib.find("strlen").orElseThrow();
        var strlenSig = FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
        var strlenMethod = Linker.nativeLinker() .downcallHandle(foreignFuncAddr, strlenSig);
        var ret = (long) strlenMethod.invokeExact(arena.allocateFrom("Bambi"));
        System.out.println("Return value " + ret); // 5
    }
}

// Your own C library needs no special Java annotations:
// long greet(char* name) {
//     printf("Hello %s\n", name);
//     return 0;
// }
发现此代码有问题? 告诉我们。
👁

C 代码保持纯 C

C 函数无需 JNI 注解或 JNIEnv 样板——任何现有的 C 库都可以直接调用。

更灵活

无需编写适配器代码或生成头文件,即可直接调用大多数现有的 C/C++ 库。

🛠️

工作流更简便

无需停下来运行 javac -h,也无需实现生成的 .h 文件中定义的接口。

旧方式
JNI(Java 原生接口)
现代方式
FFM(外部函数与内存 API)
自 JDK
22
难度
高级
从 Java 调用 C 代码
可用

在 JDK 22 中标准化(2024 年 3 月);此前自 JDK 14 起处于孵化阶段

Java 有两种调用原生 C/C++ 代码的方式:传统的 JNI 和现代的 FFM API。使用 JNI,需要将方法声明为 native,运行 javac -h 生成 C 头文件,然后使用繁琐的 JNI C API(JNIEnv、jstring 等)实现函数。FFM 在 Java 22 中作为标准 API 引入,消除了这一切:C 代码就是普通的 C——无需 JNI 约定。这使得调用现有 C/C++ 库无需修改变得更加容易。Java 端使用 Arena 进行安全的堆外内存管理,使用 MethodHandle 进行降调用,确保灵活性和安全性。