Working with Foreign Function and Memory API (Project Panama)
1. Understanding FFM API Basics
| Aspect | Detail |
|---|---|
| Status | Final since Java 22 (JEP 454) |
| Package | java.lang.foreign |
| Replaces | sun.misc.Unsafe + JNI for many use cases |
| Module | java.base (no extra requires) |
| Native access | Requires --enable-native-access=M |
2. Managing Off-Heap Memory (MemorySegment)
Example: Allocate and write
try (Arena arena = Arena.ofConfined()) {
MemorySegment seg = arena.allocate(1024);
seg.set(ValueLayout.JAVA_INT, 0L, 42);
int x = seg.get(ValueLayout.JAVA_INT, 0L);
}
| Source | API |
|---|---|
| Off-heap | arena.allocate(size) |
| Heap-backed | MemorySegment.ofArray(arr) |
| From buffer | MemorySegment.ofBuffer(bb) |
| Mapped file | FileChannel.map(...) returns MemorySegment |
3. Using Arena for Memory Management
| Arena | Lifetime |
|---|---|
Arena.global() | Forever — never freed |
Arena.ofAuto() | GC-managed |
Arena.ofConfined() | Single thread, try-with-resources |
Arena.ofShared() | Multi-thread, explicit close |
4. Defining Memory Layouts
Example: Struct layout
StructLayout point = MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("x"),
ValueLayout.JAVA_INT.withName("y")
);
VarHandle xh = point.varHandle(MemoryLayout.PathElement.groupElement("x"));
| Layout | Use |
|---|---|
| ValueLayout | Primitives (JAVA_INT, JAVA_LONG, ADDRESS) |
| SequenceLayout | Arrays |
| StructLayout / UnionLayout | Composites |
| PaddingLayout | Alignment fillers |
5. Calling Native Functions (MethodHandle)
Example: strlen call
Linker linker = Linker.nativeLinker();
SymbolLookup libc = linker.defaultLookup();
MethodHandle strlen = linker.downcallHandle(
libc.find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
try (Arena arena = Arena.ofConfined()) {
MemorySegment cstr = arena.allocateUtf8String("hello");
long len = (long) strlen.invoke(cstr);
}
| Concept | Use |
|---|---|
| Downcall | Java → native |
| Upcall | Native callback → Java |
| FunctionDescriptor | Signature |
6. Handling Pointers and Structs
| Concept | Detail |
|---|---|
| ADDRESS layout | Native pointer (size = platform word) |
| Reinterpret | seg.reinterpret(size) for unbounded ptrs |
| Strings | arena.allocateUtf8String / seg.getUtf8String(0) |
| Struct field access | VarHandle from layout path |
7. Using jextract Tool
| Aspect | Detail |
|---|---|
| Input | C header (.h) |
| Output | Java bindings (MethodHandles, layouts) |
| Command | jextract -t com.acme.libfoo --header lib/foo.h |
| Distribution | Separate downloadable tool (JEP) |
8. Understanding Safety Guarantees
| Guarantee | Detail |
|---|---|
| Bounds | Segment carries size — out-of-bounds throws |
| Lifetime | Closed Arena → IllegalStateException on access |
| Confinement | Wrong-thread access throws WrongThreadException |
| Native escape | Module must be granted with --enable-native-access |
9. Comparing with JNI
| Aspect | FFM | JNI |
|---|---|---|
| Glue code | None (pure Java) | C/C++ shim required |
| Safety | Bounded segments | Manual |
| Performance | Lower call overhead | Higher |
| Build complexity | Java-only | Native toolchain needed |
10. Understanding Performance Benefits
| Win | Source |
|---|---|
| Lower call overhead | JIT can inline downcall stubs |
| Off-heap without GC | Predictable, no copy |
| Zero-copy interop | Mapped files / direct buffers shareable |
| SIMD potential | Pair with Vector API (incubator) |