How to Call Native C Functions from Java with Project Panama (JDK 19) – A Step‑by‑Step Guide

This article introduces Java's Project Panama foreign function and memory API, showing how to use JDK 19 to call native C code from a simple Hello World program, covering linker setup, symbol lookup, function descriptors, memory sessions, and method handles with practical code examples.

Programmer DD
Programmer DD
Programmer DD
How to Call Native C Functions from Java with Project Panama (JDK 19) – A Step‑by‑Step Guide

With the release of JDK 19, it is time to explore Project Panama, specifically the new Foreign Function & Memory API that simplifies interoperability between Java and native code.

Project Overview

The Panama project aims to bridge the JVM with native code written in languages such as C/C++. It consists of three parts:

Foreign Function & Memory API (JEP 424)

Jextract tool

Vector API (JEP 338)

Hello World Example

The tutorial uses a simple Java "Hello World" application that calls a native C printf function to demonstrate the API.

Linker

The linker acts as a bridge between the JVM and native C ABI. JDK 19 provides platform‑specific linker implementations:

public static Linker getSystemLinker() { return switch (CABI.current()) { case Win64 -> Windowsx64Linker.getInstance(); case SysV -> SysVx64Linker.getInstance(); case LinuxAArch64 -> LinuxAArch64Linker.getInstance(); case MacOsAArch64 -> MacOsAArch64Linker.getInstance(); }; }

Downcalls invoke native functions, while upcalls allow native code to call back into Java.

Finding the Native Function Address

Linker linker = Linker.nativeLinker();
SymbolLookup linkerLookup = linker.defaultLookup();
SymbolLookup systemLookup = SymbolLookup.loaderLookup();
SymbolLookup symbolLookup = name -> systemLookup.lookup(name).or(() -> linkerLookup.lookup(name));
Optional<MemorySegment> printfMemorySegment = symbolLookup.lookup("printf");

Proper error handling is required because symbol lookup may fail.

Creating a Function Descriptor

FunctionDescriptor printfDescriptor = FunctionDescriptor.of(JAVA_INT, ADDRESS);

The descriptor defines a function returning an int and accepting a pointer (the C char* argument).

Building a Method Handle

MethodHandle printfMethodHandle = symbolLookup.lookup("printf")
    .map(addr -> linker.downcallHandle(addr, printfDescriptor))
    .orElse(null);

This creates an executable reference to the native printf function.

Memory Allocation

Memory for native calls is allocated outside the Java heap using MemorySession or SegmentAllocator, which implement AutoCloseable for automatic cleanup.

try (var memorySession = MemorySession.openConfined()) {
    SegmentAllocator allocator = SegmentAllocator.newNativeArena(memorySession);
    var cStringFromAllocator = allocator.allocateUtf8String("Hello World
");
    var cStringFromSession = memorySession.allocateUtf8String("Hello World
");
}

Calling the Native Function

private static int printf(String str, MemorySession memorySession) throws Throwable {
    Objects.requireNonNull(printfMethodHandle);
    var cString = memorySession.allocateUtf8String(str + "
");
    return (int) printfMethodHandle.invoke(cString);
}

public static void main(String[] args) throws Throwable {
    var str = "Hello World";
    try (var memorySession = MemorySession.openConfined()) {
        System.out.println(printf(str, memorySession));
    }
}

Summary

The article covered the Foreign Function & Memory API, showing how to locate native symbols, define function descriptors, allocate off‑heap memory, and invoke native functions from Java using method handles. The jextract tool can automate much of the boilerplate.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaforeign function APIProject Panamajdk19Memory APINative Interop
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.