Terminology:
- downcall method handle: call native functions from Java
- upcall stub: call Java functions from native code
Ingedients:
Linker.nativeLinker()returns a platform-specific Linker- produces method handles given the adress of functions and their descriptors
SymbolLookupretrieves the adress of a symbol in a library- symbols can be functions or global variables
- the adress of a symbol is modelled as a zero-length
MemorySegment Linker#defaultLookupreturns the standard C library symbols loaded with the Java runtimeSymbolLookup#libraryLookupcan load other libraries
Example:
public static void main(String[] args) throws Throwable {
// default lookup contains standard C library loaded with the Java runtime
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
// search for `strlen` and describe its input and output types
MemorySegment strlenAddress = stdlib.find("strlen").orElseThrow();
FunctionDescriptor descriptor = FunctionDescriptor.of(
ValueLayout.JAVA_LONG, // result
ValueLayout.ADDRESS // input
);
// method handle represents native function in Java
MethodHandle strlen = linker.downcallHandle(strlenAddress, descriptor);
// allocate off-heap memory and call function
try (Arena arena = Arena.ofConfined()) {
MemorySegment str = arena.allocateFrom("Hello");
long len = (long) strlen.invoke(str);
System.out.println(STR."Length = \{len}"); // 5
}
}References:
- JEP 454: Foreign Function & Memory API
- Java 22 release notes
- Javadoc Linker: contains
strlenexample - Javadoc SymbolLookup: explains how native methods are represented in Java
- Blog post by “Internal Pointers” about static and dynamic libraries
- Blog post by Clark Kromenaker about dynamic library loading on MacOS