name: java-ffm description: "Use for Java 26 Foreign Function & Memory API (FFM, Project Panama): java.lang.foreign, native library calls, off-heap memory, MemorySegment, Arena, Linker, SymbolLookup, FunctionDescriptor, MemoryLayout, upcalls/downcalls, jextract, errno, restricted native access." license: CC-BY-4.0-summary compatibility: "Java SE 26 / JDK 26; java.lang.foreign stable since Java 22" metadata: sources: - "https://openjdk.org/jeps/454" - "https://openjdk.org/jeps/472" - "https://docs.oracle.com/en/java/javase/26/core/foreign-function-and-memory-api.html" - "https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/lang/foreign/package-summary.html"
Java 26 FFM Skill
Use this skill when writing/reviewing Java code using the Foreign Function & Memory API.
When active:
- Identify C ABI/platform, exact native signatures, library loading path, ownership/lifetime, thread access, and error convention.
- Prefer safe FFM idioms: arena-bound allocation, explicit layouts, exact method handle types, explicit cleanup, minimal restricted API use.
- Read
references/java-26-ffm.mdfor distilled API facts, patterns, and pitfalls. - If implementing bindings for many C declarations, recommend
jextract; hand-code only small bindings.
Core model
MemorySegment= contiguous memory view: address + byte size + scope + access checks.Arena= native memory lifetime owner. Close arena → all segments allocated by it invalid.MemoryLayout/ValueLayout/AddressLayout= data shape, size, alignment, endianness, access handles.Linker+SymbolLookup+FunctionDescriptor= turn native symbol + signature intoMethodHandledowncall.- Upcall = native code calls Java via
linker.upcallStub(methodHandle, descriptor, arena).
Default imports
import java.lang.foreign.*;
import java.lang.invoke.*;
import static java.lang.foreign.ValueLayout.*;
import static java.lang.foreign.MemoryLayout.*;
import static java.lang.foreign.MemoryLayout.PathElement.*;
Native access
Restricted FFM methods need native access enabled.
FFM is final in Java 26; do not add --enable-preview for FFM-only code.
javac --release 26 Main.java
java --enable-native-access=ALL-UNNAMED Main
java --enable-native-access=my.module Main
In JDK 24+, illegal native access defaults to warning; future JDK may deny.
For CI, prefer explicit --enable-native-access=<module> and consider
--illegal-native-access=deny.
Fast checklists
Before downcall:
- Exact C prototype? Include typedef expansion, pointer depth, varargs specialization.
- Correct platform layouts?
longdiffers: Linux/x64long= 64-bit; Windows/x64long= 32-bit.size_tisJAVA_LONGon Linux/x64, notJAVA_INT. - C constants/macros?
Do not guess magic numbers; use
jextract, generate constants, or choose APIs without macro constants. - Ownership? Who allocates/frees returned pointers? Which arena owns passed buffers/upcall stubs?
- Threading? Confined arena only owner thread. Shared arena for cross-thread access.
- Error path?
Return sentinel?
errno? output parameter?
Before structs:
- Include explicit padding where C ABI inserts it.
- Verify
byteSize()andbyteAlignment()vs Csizeof/alignofif possible. - Use named layouts + layout path var handles; avoid manual offset math.
Before pointer dereference/out params:
- Pointer from native/read memory is often zero-length.
- C
T** outmeans allocate anADDRESSslot, pass that slot, then read the realT*withslot.get(ADDRESS, 0); use the real handle for later calls, not the address of the slot. Common example:sqlite3_open(..., sqlite3**). - Attach bounds via
AddressLayout.withTargetLayout(...)when statically known, orsegment.reinterpret(size, arena, cleanup)when dynamically known. - Treat
reinterpretand target layouts as unsafe/restricted: wrong size/lifetime can crash JVM.
Canonical minimal downcall
static final Linker LINKER = Linker.nativeLinker();
static final SymbolLookup LIBC = LINKER.defaultLookup();
static final MethodHandle strlen = LINKER.downcallHandle(
LIBC.findOrThrow("strlen"),
FunctionDescriptor.of(JAVA_LONG, ADDRESS) // Linux/x64 size_t, char*
);
static long strlen(String s) throws Throwable {
try (Arena arena = Arena.ofConfined()) {
MemorySegment cString = arena.allocateFrom(s); // UTF-8, NUL-terminated
return (long) strlen.invokeExact(cString);
}
}
Examples
Read examples/jep-454.md for JEP-derived snippets.
Primary references
Open references/java-26-ffm.md for:
- API map
- arena/segment rules
- type mapping
- downcall/upcall/variadic/errno patterns
- pointer-return handling
- struct layouts
- jextract workflow
- safety pitfalls