Memory Segments and Arenas

You can access off-heap or on-heap memory with the Foreign Function and Memory (FFM) API through the MemorySegment interface. Each memory segment is associated with, or backed by, a contiguous region of memory. There are two kinds of memory segments:

  • Heap segment: This is a memory segment backed by a region of memory inside the Java heap, an on-heap region.
  • Native segment: This is a memory segment backed by a region of memory outside the Java heap, an off-heap region. The examples in this chapter demonstrate how to allocate and access native segments.

An arena controls the lifecycle of native memory segments. To create an arena, use one of the methods in the Arena interface, such as Arena.ofConfined(). You use an arena to allocate a memory segment. Each arena has a scope, which specifies when the region of memory that backs the memory segment will be deallocated and is no longer valid. A memory segment can only be accessed if the scope associated with it is still valid or alive.

Most of the examples described in this chapter use a confined arena, which is created with Arena::ofConfined. A confined arena provides a bounded and deterministic lifetime. Its scope is alive from when it's created to when it's closed. A confined arena has an owner thread. This is typically the thread that created it. Only the owner thread can access the memory segments allocated in a confined arena. You'll get an exception if you try to close a confined arena with a thread other than the owner thread.

There are other kinds of arenas:

  • A shared arena, which is created with Arena::ofShared, has no owner thread. Multiple threads may access the memory segments allocated in a shared arena. In addition, any thread may close a shared arena, and the closure is guaranteed to be safe and atomic. See Slicing Memory Segments for an example of a shared arena.
  • An automatic arena, which is created with Arena::ofAuto. This is an area that's managed, automatically, by the garbage collector. Any thread can access memory segments allocated by an automatic arena. If you call Arena::close on an automatic arena, you'll get a UnsupportedOperationException.
  • A global arena, which is created with Arena::global. Any thread can access memory segments allocated with this arena. In addition, the region of memory of these memory segments is never deallocated; if you call Arena::close on a global arena, you'll get a UnsupportedOperationException.

The following example allocates a memory segment with an arena, stores a Java String in the off-heap memory associated with the memory segment, and then prints the contents of the off-heap memory. At the end of the try-with-resources block, the arena is closed, and the off-heap memory associated with the memory segment is deallocated.

    String s = "My string";
    try (Arena arena = Arena.ofConfined()) {

        // Allocate off-heap memory
        MemorySegment nativeText = arena.allocateFrom(s);

        // Access off-heap memory
        for (int i = 0; i < s.length(); i++ ) {
           System.out.print((char)nativeText.get(ValueLayout.JAVA_BYTE, i)); 
        }            
    } // Off-heap memory is deallocated 

The following sections describe this example in detail:

Allocating a Memory Segment with an Arena and Storing a String in It

The Arena interface extends the SegmentAllocator interface, which contains methods that both allocate off-heap memory and copy Java data into it. The previous example calls the method SegmentAllocator.allocateFrom(String), which allocates a memory segment with an arena, converts a string into a UTF-8 encoded, null-terminated C string, and then stores the string into the memory segment.

    String s = "My string";
    try (Arena arena = Arena.ofConfined()) {

        // Allocate off-heap memory
        MemorySegment nativeText = arena.allocateFrom(s);
        // ...

Tip:

You can call SegmentAllocator.allocateFrom(String, Charset) to store a string with a different charset. The SegmentAllocator interface contains several allocateFrom methods that enable you to store data of various data types in a memory segment.

See Memory Layouts and Structured Access for information about allocating and accessing more complicated native data types such as C structures.

Printing the Contents of Off-Heap Memory

The following code prints the characters stored in the MemorySegment named nativeText:

            // Access off-heap memory
            for (int i = 0; i < s.length(); i++ ) {
               System.out.print((char)nativeText.get(ValueLayout.JAVA_BYTE, i)); 
            }

The MemorySegment interface contains various access methods that enable you to read from or write to memory segments. Each access method takes as an argument a value layout, which models the memory layout associated with values of basic data types such as primitives. A value layout encodes the size, the endianness or byte order, the bit alignment of the piece of memory to be accessed, and the Java type to be used for the access operation.

For example, MemoryLayout.get(ValueLayout.OfByte,long) takes as an argument ValueLayout.JAVA_BYTE. This value layout has the following characteristics:

  • The same size as a Java byte
  • Byte alignment set to 1: This means that the memory layout is stored at a memory address that's a multiple of 8 bits.
  • Byte order set to ByteOrder.nativeOrder(): A system can order the bytes of a multibyte value from most significant to least significant (big-endian) or from least significant to most significant (little-endian).

Closing an Arena

When an arena is closed, such as through a try-with-resources statement, then the arena's scope is no longer alive: All memory segments associated with its scope are invalidated, and the memory regions backing them are deallocated.

If you try to access a memory segment associated with an arena scope that's closed, you'll get an IllegalStateException, which the following example demonstrates:

    String s = "My String";    
    MemorySegment nativeText;
    try (Arena arena = Arena.ofConfined()) {

        // Allocate off-heap memory
        nativeText = arena.allocateFrom(s);
    }
    for (int i = 0; i < s.length(); i++ ) {
        // Exception in thread "main" java.lang.IllegalStateException: Already closed
        System.out.print((char)nativeText.get(ValueLayout.JAVA_BYTE, i)); 
    }