Recent Posts
Archives

Posts Tagged ‘ProjectPanama’

PostHeaderIcon [DevoxxBE2024] Project Panama in Action: Building a File System by David Vlijmincx

At Devoxx Belgium 2024, David Vlijmincx delivered an engaging session on Project Panama, demonstrating its power by building a custom file system in Java. This practical, hands-on talk showcased how Project Panama simplifies integrating C libraries into Java applications, replacing the cumbersome JNI with a more developer-friendly approach. By leveraging Fuse, virtual threads, and Panama’s memory management capabilities, David walked attendees through creating a functional file system, highlighting real-world applications and performance benefits. His talk emphasized the ease of integrating C libraries and the potential to build high-performance, innovative solutions.

Why Project Panama Matters

David began by addressing the challenges of JNI, which many developers find frustrating due to its complexity. Project Panama, part of OpenJDK, offers a modern alternative for interoperating with native C libraries. With a vast ecosystem of specialized C libraries—such as io_uring for asynchronous file operations or libraries for AI and keyboard communication—Panama enables Java developers to access functionality unavailable in pure Java. David demonstrated this by comparing file reading performance: using io_uring with Panama, he read files faster than Java’s standard APIs (e.g., BufferedReader or Channels) in just two nights of work, showcasing Panama’s potential for performance-critical applications.

Building a File System with Fuse

The core of David’s demo was integrating the Fuse (Filesystem in Userspace) library to create a custom file system. Fuse acts as a middle layer, intercepting commands like ls from the terminal and passing them to a Java application via Panama. David explained how Fuse provides a C struct that Java developers can populate with pointers to Java methods, enabling seamless communication between C and Java. This struct, filled with method pointers, is mounted to a directory (e.g., ~/test), allowing the Java application to handle file system operations transparently to the user, who sees only the terminal output.

Memory Management with Arenas

A key component of Panama is its memory management via arenas, which David used to allocate memory for passing strings to Fuse. He demonstrated using Arena.ofShared(), which allows memory sharing across threads and explicit lifetime control via try-with-resources. Other arena types, like Arena.ofConfined() (single-threaded) or Arena.global() (unbounded lifetime), were mentioned for context. David allocated a memory segment to store pointers to a string array (e.g., ["-f", "-d", "~/test"]) and used Arena.allocateFrom() to create C-compatible strings. This ensured safe memory handling when interacting with Fuse, preventing leaks and simplifying resource management.

Downcalls and Upcalls: Bridging Java and C

David detailed the process of making downcalls (Java to C) and upcalls (C to Java). For downcalls, he created a function descriptor mirroring the C method’s signature (e.g., fuse_main_real, returning an int and taking parameters like string arrays and structs). Using Linker.nativeLinker(), he generated a platform-specific linker to invoke the C method. For upcalls, he recreated Fuse’s struct in Java using MemoryLayout.structLayout, populating it with pointers to Java methods like getattr. Tools like JExtract simplified this by generating bindings automatically, reducing boilerplate code. David showed how JExtract creates Java classes from C headers, though it requires an additional abstraction layer for user-friendly APIs.

Implementing File System Operations

David implemented two file system operations: reading files and creating directories. For reading, he extracted the file path from a memory segment using MemorySegment.getString(), checked if it was a valid file, and copied file contents into a buffer with MemorySegment.reinterpret() to handle size constraints. For directory creation, he added paths to a map, demonstrating simplicity. Running the application mounted the file system to ~/test, where commands like mkdir and echo worked seamlessly, with Fuse calling Java methods via upcalls. David unmounted the file system, showing its clean integration. Performance tips included reusing method handles and memory segments to avoid overhead, emphasizing careful memory management.

Links:

PostHeaderIcon [DevoxxBE2023] The Panama Dojo: Black Belt Programming with Java 21 and the FFM API by Per Minborg

In an engaging session at Devoxx Belgium 2023, Per Minborg, a Java Core Library team member at Oracle and an OpenJDK contributor, guided attendees through the intricacies of the Foreign Function and Memory (FFM) API, a pivotal component of Project Panama. With a blend of theoretical insights and live coding, Per demonstrated how this API, in its third preview in Java 21, enables seamless interaction with native memory and functions using pure Java code. His talk, dubbed the “Panama Dojo,” showcased the API’s potential to enhance performance and safety, culminating in a hands-on demo of a lightweight microservice framework built with memory segments, arenas, and memory layouts.

Unveiling the FFM API’s Capabilities

Per introduced the FFM API as a solution to the limitations of Java Native Interface (JNI) and direct buffers. Unlike JNI, which requires cumbersome C stubs and inefficient data passing, the FFM API allows direct native memory access and function calls. Per illustrated this with a Point struct example, where a memory segment models a contiguous memory region with 64-bit addressing, supporting both heap and native segments. This eliminates the 2GB limit of direct buffers, offering greater flexibility and efficiency.

The API introduces memory segments with constraints like size, lifetime, and thread confinement, preventing out-of-bounds access and use-after-free errors. Per highlighted the importance of deterministic deallocation, contrasting Java’s automatic memory management with C’s manual approach. The FFM API’s arenas, such as confined and shared arenas, manage segment lifecycles, ensuring resources are freed explicitly, as demonstrated in a try-with-resources block that deterministically deallocates a segment.

Structuring Memory with Layouts and Arenas

Memory layouts, a key FFM API feature, provide a declarative way to define memory structures, reducing manual offset computations. Per showed how a Point layout with x and y doubles uses var handles to access fields safely, leveraging JIT optimizations for atomic operations. This approach minimizes bugs in complex structs, as var handles inherently account for offsets, unlike manual calculations.

Arenas further enhance safety by grouping segments with shared lifetimes. Per demonstrated a confined arena, restricting access to a single thread, and a shared arena, allowing multi-threaded access with thread-local handshakes for safe closure. These constructs bridge the gap between C’s flexibility and Rust’s safety, offering a balanced model for Java developers. In his live demo, Per used an arena to allocate a MarketInfo segment, showcasing deterministic deallocation and thread safety.

Building a Persistent Queue with Memory Mapping

The heart of Per’s session was a live coding demo constructing a persistent queue using memory mapping and atomic operations. He defined a MarketInfo record for stock exchange data, including timestamp, symbol, and price fields. Using a record mapper, Per serialized and deserialized records to and from memory segments, demonstrating immutability and thread safety. The mapper, a potential future JDK feature, simplifies data transfer between Java objects and native memory.

Per then implemented a memory-mapped queue, where a file-backed segment stores headers and payloads. Headers use atomic operations to manage mutual exclusion across threads and JVMs, ensuring safe concurrent access. In the demo, a producer appended MarketInfo records to the queue, while two consumers read them asynchronously, showcasing low-latency, high-performance data sharing. Per’s use of sparse files allowed a 1MB queue to scale virtually, highlighting the API’s efficiency.

Crafting a Microservice Framework

The session culminated in assembling these components into a microservice framework. Per’s queue, inspired by Chronicle Queue, supports persistent, high-performance data exchange across JVMs. The framework leverages memory mapping for durability, atomic operations for concurrency, and record mappers for clean data modeling. Per demonstrated its practical application by persisting a queue to a file and reading it in a separate JVM, underscoring its robustness for distributed systems.

He emphasized the reusability of these patterns across domains like machine learning and graphics processing, where native libraries are prevalent. Tools like jextract, briefly mentioned, further unlock native libraries like TensorFlow, enabling Java developers to integrate them effortlessly. Per’s framework, though minimal, illustrates how the FFM API can transform Java’s interaction with native code, offering a safer, faster alternative to JNI.

Performance and Safety in Harmony

Throughout, Per stressed the FFM API’s dual focus on performance and safety. Native function calls, faster than JNI, and memory segments with strict constraints outperform direct buffers while preventing common errors. The API’s integration with existing JDK features, like var handles, ensures compatibility and optimization. Per’s live coding, despite its complexity, flowed seamlessly, reinforcing the API’s practicality for real-world applications.

Conclusion: Embracing the Panama Dojo

Per’s session was a masterclass in leveraging the FFM API to push Java’s boundaries. By combining memory segments, layouts, arenas, and atomic operations, he crafted a framework that exemplifies the API’s potential. His call to action—experiment with the FFM API in Java 21—invites developers to explore this transformative tool, promising enhanced performance and safety for native interactions. The Panama Dojo left attendees inspired to break new ground in Java development.

Links: