|
| 1 | +--- |
| 2 | +id: 113 |
| 3 | +slug: "call-c-from-java" |
| 4 | +title: "Calling out to C code from Java" |
| 5 | +category: "language" |
| 6 | +difficulty: "advanced" |
| 7 | +jdkVersion: "22" |
| 8 | +oldLabel: "Java 1.1+" |
| 9 | +modernLabel: "Java 22+" |
| 10 | +oldApproach: "JNI (Java Native Interface)" |
| 11 | +modernApproach: "FFM (Foreign Function & Memory API)" |
| 12 | +oldCode: |- |
| 13 | + public class CallCFromJava { |
| 14 | + static { System.loadLibrary("strlen-jni"); } |
| 15 | + public static native long strlen(String s); |
| 16 | + public static void main(String[] args) { |
| 17 | + long ret = strlen("Bambi"); |
| 18 | + System.out.println("Return value " + ret); // 5 |
| 19 | + } |
| 20 | + } |
| 21 | +
|
| 22 | + // Run javac -h to generate the .h file, then write C: |
| 23 | + // #include "CallCFromJava.h" |
| 24 | + // #include <string.h> |
| 25 | + // JNIEXPORT jlong JNICALL Java_CallCFromJava_strlen( |
| 26 | + // JNIEnv *env, jclass clazz, jstring str) { |
| 27 | + // const char* s = (*env)->GetStringUTFChars(env, str, NULL); |
| 28 | + // jlong len = (jlong) strlen(s); |
| 29 | + // (*env)->ReleaseStringUTFChars(env, str, s); |
| 30 | + // return len; |
| 31 | + // } |
| 32 | +modernCode: |- |
| 33 | + void main() throws Throwable { |
| 34 | + try (var arena = Arena.ofConfined()) { |
| 35 | + // Use any system library directly — no C wrapper needed |
| 36 | + var stdlib = Linker.nativeLinker().defaultLookup(); |
| 37 | + var foreignFuncAddr = stdlib.find("strlen").orElseThrow(); |
| 38 | + var strlenSig = FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS); |
| 39 | + var strlenMethod = Linker.nativeLinker() .downcallHandle(foreignFuncAddr, strlenSig); |
| 40 | + var ret = (long) strlenMethod.invokeExact(arena.allocateFrom("Bambi")); |
| 41 | + IO.println("Return value " + ret); // 5 |
| 42 | + } |
| 43 | + } |
| 44 | +
|
| 45 | + // Your own C library needs no special Java annotations: |
| 46 | + // long greet(char* name) { |
| 47 | + // printf("Hello %s\n", name); |
| 48 | + // return 0; |
| 49 | + // } |
| 50 | +summary: "FFM lets Java call C libraries directly, without JNI boilerplate or C-side Java knowledge." |
| 51 | +explanation: "Java has two approaches for calling native C/C++ code: the traditional\ |
| 52 | + \ JNI and the modern FFM API. With JNI, you declare a method as native, run javac -h\ |
| 53 | + \ to generate a C header file, then implement the function using the cumbersome JNI\ |
| 54 | + \ C API (JNIEnv, jstring, etc.). FFM, introduced as a standard API in Java 22,\ |
| 55 | + \ eliminates all of that: C code is just plain C — no JNI conventions needed. This\ |
| 56 | + \ makes it far easier to call existing C/C++ libraries without modification. The\ |
| 57 | + \ Java side uses Arena for safe off-heap memory management and MethodHandle for the\ |
| 58 | + \ downcall, ensuring both flexibility and safety." |
| 59 | +whyModernWins: |
| 60 | +- icon: "👁" |
| 61 | + title: "C code stays plain C" |
| 62 | + desc: "The C function requires no JNI annotations or JNIEnv boilerplate — any existing C library can be called as-is." |
| 63 | +- icon: "⚡" |
| 64 | + title: "More flexible" |
| 65 | + desc: "Directly call most existing C/C++ libraries without writing adapter code or generating header files." |
| 66 | +- icon: "🛠️" |
| 67 | + title: "Easier workflow" |
| 68 | + desc: "No need to stop, run javac -h, and implement the interface defined in the generated .h file." |
| 69 | +support: |
| 70 | + state: "available" |
| 71 | + description: "Standardized in JDK 22 (March 2024); previously incubating since JDK 14" |
| 72 | +prev: "language/compact-canonical-constructor" |
| 73 | +next: "enterprise/servlet-vs-jaxrs" |
| 74 | +related: |
| 75 | +- "io/file-memory-mapping" |
| 76 | +- "language/compact-source-files" |
| 77 | +- "language/unnamed-variables" |
| 78 | +docs: |
| 79 | +- title: "JEP 454: Foreign Function & Memory API" |
| 80 | + href: "https://openjdk.org/jeps/454" |
| 81 | +- title: "java.lang.foreign package (Java 22)" |
| 82 | + href: "https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/foreign/package-summary.html" |
0 commit comments