Package org.apache.datasketches.memory

This package provides high performance primitive and primitive array access to direct (native), off-heap memory and memory-mapped file resources, consistent views into ByteBuffer, and on-heap primitive arrays. It can be used as a more comprehensive and flexible replacement for ByteBuffer.

In addition, this package provides:

  • Two different access APIs: read-only Memory and WritableMemory for absolute offset access, and read-only Buffer and WritableBuffer for relative positional access (similar to ByteBuffer).
  • Clean separation of Read-only API from Writable API, which makes writable versus read-only resources detectable at compile time.
  • The conversion from Writable to read-only is just a cast, so no unnecessary objects are created. For example:
         WritableMemory wMem = ...
         Memory mem = wMem;
     
  • AutoCloseable for the external resources that require it, which enables compile-time checks for non-closed resources.
  • Immediate invalidation of all downstream references of an AutoCloseable resource when that resource is closed, either manually or by the JVM. This virtually eliminates the possibility of accidentally writing into the memory space previously owned by a closed resource.
  • Improved performance over the prior Memory implementation.
  • Cleaner internal architecture, which will make it easier to extend in the future.
  • No external dependencies, which makes it simple to install in virtually any Java environment.

More specifically, this package provides access to four different types of resources using two different access APIs. These resources can be viewed as contiguous blobs of bytes that provide at least byte-level read and write access. The four resources are:

  • Direct (a.k.a. Native) off-heap memory allocated by the user.
  • Memory-mapped files, both writable and read-only.
  • ByteBuffers, both heap-based and direct, writable and read-only.
  • Heap-based primitive arrays, which can be accessed as writable or read-only.

The two different access APIs are:

  • Memory, WritableMemory: Absolute offset addressing into a resource.
  • Buffer, WritableBuffer: Position relative addressing into a resource.

In addition, all combinations of access APIs and backing resources can be accessed via multibyte primitive methods (e.g. getLong(...), getLongArray(...), putLong(...), putLongArray(...)) as either ByteOrder.BIG_ENDIAN or ByteOrder.LITTLE_ENDIAN.

The resources don't know or care about the access APIs, and the access APIs don't really know or care what resource they are accessing.

A Direct or memory-mapped file resource can also be explicitly closed by the user

     //Using try-with-resources block:
     try (WritableMemory wmem = WritableMemory.map(File file)) {
       doWork(wMem) // read and write to memory mapped file.
     }

     //Using explicit close():
     WritableMemory wmem = WritableMemory.map(File file);
     doWork(wMem) // read and write to memory mapped file.
     wmem.close();
 

Whatever thread of your process is responsible for allocating a direct or memory-mapped resource must be responsible for closing it or making sure it gets closed. This is also true for the special memory-mapping methods load(), isLoaded() and force().

Moving back and forth between Memory and Buffer:

    Memory mem = ...
    Buffer buf = mem.asBuffer();
    ...
    Memory mem2 = buf.asMemory();
    ...
 

Hierarchical memory regions can be easily created:

     WritableMemory wMem = ...
     WritableMemory wReg = wMem.writableRegion(offset, length); //OR
     Memory region = wMem.region(offset, length);
 

All methods are checked for bounds violations.

The classes in this package are not thread-safe.

Author:
Lee Rhodes