Programming Persistent Memory With Java

ID 660100
Updated 6/5/2024
Version Latest
Public

author-image

By

Introduction

Intel has taken persistent memory programming with Java to the next level with the 1.0 release of the Low Level Persistent Library (LLPL) for Java. LLPL is an open source Java library that gives Java developers access to persistent memory in a fast and flexible way. LLPL adds to the many other resources for persistent memory programming and is part of the Persistent Memory Development Kit (PMDK). PMDK, though maintained by Intel, is based on (Storage Networking Industry Association) SNIA industry standards. In addition to being part of PMDK, LLPL depends on two PMDK libraries, libpmem and libpmemobj. LLPL aims to provide flexible, high-performance access to persistent memory from Java with things like heaps, memory blocks, and transactions. 

This article introduces some of the new concepts of LLPL and links to examples and resources to learn more. The concepts discussed in this article build upon persistent memory programming foundations such as transactions and flushing. For a refresher on those concepts, check out our Quick Start Guide, or chapter 7 of Programming Persistent Memory, A Comprehensive Guide for Developers, which is available for purchase or free download. 

Heaps, Memory Blocks, and Transactions

An LLPL heap is a pool of persistent memory and an allocator for it. You can create LLPL heaps, reopen them, and delete them. You can create many heaps of almost any size, as long as there is unused persistent memory available to create them. Unlike Java heaps, LLPL heaps are persistent, so even after a restart, you can access the heap and their memory blocks. Additionally, the heap API is thread-safe.

When you allocate memory in LLPL, what you get back is a MemoryBlock object, which provides an accessor API for the allocation. 

To create a heap and allocate memory block:

Heap heap = Heap.createHeap("/pmem/heap1", 100 * 1024 * 1024);
MemoryBlock block1 = heap.allocateMemoryBlock(1024, false);

In Java, garbage collection will automatically deallocate unreachable memory, but in LLPL, you must call the MemoryBlock free()method to deallocate memory manually.

The MemoryBlock API provides low-level setter and getter methods. Locations within a memory block to write or read are specified using a zero-based long offset from the beginning of the block. The MemoryBlock API does no locking, so developers have the freedom to create whatever concurrency scheme appropriate for their application.

You can ask a memory block for its numeric name, known as its "handle," and write this Java long handle into other memory blocks. Using a handle allows blocks to be linked, supporting the implementation of efficient reference-based data structures. Memory block handles remain valid until the block's memory is freed.

Transactions in LLPL, just like in the PMDK libpmemobj library, allow for creating fail-safe writes within an atomic aggregate. LLPL heap modifications (e.g. writes, allocations) done in a transaction behave as if they all executed together or didn't execute at all. The LLPL Transaction API integrates well with Java's exception handling and uses Java lambdas as transaction bodies. 

String s = "Saturn";
Transaction.create(heap, () -> {
   block1.addToTransaction(0, Integer.BYTES + s.length());
   block1.setInt(0, s.length());
   block1.copyFromArray(s.getBytes(), 0, 4, s.length());
});

Summary

This article highlights three primary elements of the new 1.0 release of LLPL: heaps, memory blocks, and transactions. LLPL is compatible with the Java Development Kit (JDK) 8 and higher and supports building with Maven or Make. The availability of LLPL on MavenCentral is in-progress. To learn more about LLPL, please check out the webinar, Java Programming with Persistent Memory.

Resources 

Book: Programming Persistent Memory
Webinar Programming with Persistent Memory from Java
More details about 1.0 release of LLPL on pmem.io
LLPL on GitHub
Cassandra PMEM examples on GitHub