-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathcall-c-from-java.yaml
More file actions
82 lines (80 loc) · 3.48 KB
/
call-c-from-java.yaml
File metadata and controls
82 lines (80 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
---
id: 113
slug: "call-c-from-java"
title: "Calling out to C code from Java"
category: "language"
difficulty: "advanced"
jdkVersion: "22"
oldLabel: "Java 1.1+"
modernLabel: "Java 22+"
oldApproach: "JNI (Java Native Interface)"
modernApproach: "FFM (Foreign Function & Memory API)"
oldCode: |-
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;
// }
modernCode: |-
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"));
IO.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;
// }
summary: "FFM lets Java call C libraries directly, without JNI boilerplate or C-side Java knowledge."
explanation: "Java has two approaches for calling native C/C++ code: the traditional\
\ JNI and the modern FFM API. With JNI, you declare a method as native, run javac -h\
\ to generate a C header file, then implement the function using the cumbersome JNI\
\ C API (JNIEnv, jstring, etc.). FFM, introduced as a standard API in Java 22,\
\ eliminates all of that: C code is just plain C — no JNI conventions needed. This\
\ makes it far easier to call existing C/C++ libraries without modification. The\
\ Java side uses Arena for safe off-heap memory management and MethodHandle for the\
\ downcall, ensuring both flexibility and safety."
whyModernWins:
- icon: "👁"
title: "C code stays plain C"
desc: "The C function requires no JNI annotations or JNIEnv boilerplate — any existing C library can be called as-is."
- icon: "⚡"
title: "More flexible"
desc: "Directly call most existing C/C++ libraries without writing adapter code or generating header files."
- icon: "🛠️"
title: "Easier workflow"
desc: "No need to stop, run javac -h, and implement the interface defined in the generated .h file."
support:
state: "available"
description: "Standardized in JDK 22 (March 2024); previously incubating since JDK 14"
prev: "language/compact-canonical-constructor"
next: "enterprise/servlet-vs-jaxrs"
related:
- "io/file-memory-mapping"
- "language/compact-source-files"
- "language/unnamed-variables"
docs:
- title: "JEP 454: Foreign Function & Memory API"
href: "https://openjdk.org/jeps/454"
- title: "java.lang.foreign package (Java 22)"
href: "https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/foreign/package-summary.html"