Summary
-------
Enhance class `java.nio.MappedByteBuffer` so that instances can be mapped via a file belonging to a file system mounted from an non-volatile memory (NVM) device, allowing writes to be committed via an efficient cache-line flush.
Problem
-------
NVM offers the opportunity for application programmers to create and update program state across program runs without incurring the significant copying and/or translation costs that output to and input from a persistent medium normally implies. This is particularly significant for transactional programs, where regular persistence of in-doubt state is required to enable crash recovery.
Existing C libraries (such as Intel's libpmem) provide C programs with highly efficient access to NVM at the base level. They also build on this to support simple management of a variety of persistent data types. Currently, use of even just the base library from Java is costly because of the frequent need to make system calls or JNI calls to invoke the primitive operation which ensures memory changes are persistent. The same problem limits use of the higher-level libraries and is exacerbated by the fact that the persistent data types provided in C are allocated in memory not directly accessible from Java. This places Java applications and middleware (for example, a Java transaction manager) at a severe disadvantage compared with C or languages which can link into C libraries at low cost.
This proposal attempts to remedy the first problem by allowing efficient writeback of NVM mapped to a ByteBuffer. Since ByteBuffer-mapped memory is directly accessible to Java this proposal enables the second problem to be addressed by implementing client libraries equivalent to those provided in C to manage storage of different persistent data types.
Solution
--------
A public extension enumeration `ExtendedMapMode` will be added to package `jdk.nio.mapmode` in a newly created module of the same name. Module `jdk.nio.mapmode` will export this package unconditonally. Enumeration `ExtendedMapMode` will expose two new `MapMode` enumeration values `READ_ONLY_SYNC` and `READ_WRITE_SYNC`.
Method `FileChannelImpl.map` will be modified to recognize these modes and, when specified, return a `MappedByteBuffer` that has been mapped to NVM. The file channel for the mapping must belong to a file system which can be mapped to memory using the mmap flags `MAP_SYNC` and `MAP_SHARED_VALIDATE`.
Methods `force()` and `force(long,long)` of `MappedByteBuffer` will be modified to respect the mapping of the mapped memory to NVM or volatile memory. In the former case writeback operations will proceed using a new internal API that flushes changes using cache line writeback. In the latter case the existing writeback implementation will be retained, employing file descriptor-based force operations.
Statistics for mapped NVM buffers will be published via a new `BufferPoolMXBean`. Class `ManagementFactory` provides method `List<T> getPlatformMXBeans(Class<T>)` which can be used to retrieve a list of `BufferPoolMXBean` instances tracking `count`, `total_capacity` and `memory_used` for the existing categories of mapped or direct byte buffers. It will be modified to return an extra, new `BufferPoolMXBean` with name `"mapped - 'non-volatile memory'"`, which will track the above stats for all `MappedByteBuffer` instances currently mapped with mode `ExtendedMapMode.READ_ONLY_SYNC` or `ExtendedMapMode.READ_WRITE_SYNC`. The existing `BufferPoolMXBean` with name `mapped` will continue only to track stats for `MappedByteBuffer` instances currently mapped with mode `MapMode.READ_ONLY`, `MapMode.READ_WRITE` or `MapMode.PRIVATE`.
The new `MapMode` values and new implementation of `FileChannelImpl.map` do not imply a change to the API of the `map` operation. However, they do introduce new error cases. These are specified in the javadoc of `ExtendedMapMode` but a few extra comments are required:
The new implementation of `FileChannelImpl.map` may only be implemented on specific platforms: initially this will be x86_64/Linux and AArch64/Linux. If the new `MapMode` values are passed to map on unsupported platforms an UnsupportedOperationException will be thrown.
In the case of AArch64, UnsupportedOperationException will be thrown when the current CPU does not implement the hardware instructions needed to support cache line writeback to memory (all current ARMv8.1 CPUs and, potentially, some ARMv8.2 CPUs).
The new implementation of `FileChannelImpl.map` may throw IOException, even on a supported platform. This will occur if the underlying OS implementation does not implement support for mapping into memory files from a file system associated with an NVM device. It will also happen if the file associated with the FileChannel does not belong to such a file system.
Specification
-------------
The new Map Modes are documented as follows:
/**
* Defines JDK-specific file mapping modes.
*
* @moduleGraph
* @since 14
*/
module jdk.nio.mapmode; {
exports jdk.nio.mapmode;
}
package jdk.nio.mapmode;
/**
* JDK-specific file mapping modes.
*
* @since 14
* @see java.nio.channels.FileChannel#map
*/
public class ExtendedMapMode {
private ExtendedMapMode() { }
/**
* File mapping mode for a read-only mapping of a file backed by
* non-volatile RAM.
*
* <p> The {@linkplain FileChannel#map map} method throws
* {@linkplain UnsupportedOperationException} when this map mode
* is used on an implementation that does not support it.
*
* @implNote On Linux, the {@code MAP_SYNC} and {@code
* MAP_SHARED_VALIDATE} flags are specified to {@code mmap} when
* mapping the file into memory.
*/
public static final MapMode READ_ONLY_SYNC = . . .
/**
* File mapping mode for a read-write mapping of a file backed by
* non-volatile RAM. {@linkplain MappedByteBufefr#force force}
* operations on a buffer created with this mode will be performed
* using cache line writeback rather than proceeding via a file
* device flush.
*
* <p> The {@linkplain FileChannel#map map} method throws
* {@linkplain UnsupportedOperationException} when this map mode
* is used on an implementation that does not support it.
*
* @implNote On Linux, the {@code MAP_SYNC} and {@code
* MAP_SHARED_VALIDATE} flags are specified to {@code mmap} when
* mapping the file into memory.
*/
public static final MapMode READ_WRITE_SYNC = . . .
}