Since Java 9 every new Java version has incubator modules, which contain features that are still in development and they can be tested by developers.
One of these incubators is the Foreign-Memory Access API (JEP 370) that will be introduced in this tutorial.
A memory segment can be accessed through a memory address, which will be checked beforehand if it is part of the boundary of your referenced memory segment. If you want to access an unchecked memory address and it was part of a memory segment that the client had, then you have to reinterpret the offset of the unchecked address relative to the segment's base address (rebase operation: MemoryAddress::rebase). If a memory segment like that does not exist, then you can use MemorySegment::ofNativeRestricted factory instead. This factory can be used only if the JDK property “foreign.restricted” is not set to the value “permit” and it should be used carefully.
Only one thread can access (and be owner of a) a certain memory segment at a time, which ensures "temporal safety".
Another thread gets access to a busy memory segment, after the holder (owner) of the memory segment has closed the segment. If you want to access and edit memory address parallel, then you can use a “spliterator”, which splits a memory segment in slices. Every slice is used then by certain threads to perform parts of the complete task. The memory segment cannot be closed during this process to enforce safety.
This API ensures that:
- It can operate on various types of foreign memory (example: persistent memory, managed heap memory, etc.)
- It does not undermine the safety of the "Java Runtime Machine"
- Deallocations are done explicit in your Java code
- Emphasis on usability compared to other alternatives (example: “ByteBuffer API”)
If you want to compile your application with this API, then you have to add the module "jdk.incubator.foreign".
Example (If you use the command line):
javac --add-modules jdk.incubator.foreign ForeignMemoryAccess.java
An example application with the Foreign-Memory Access API
package java14ForeignMemoryAccess;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemorySegment;
public class ForeignMemoryAccess {
public static void main(String[] args) {
int allocatedBlockSize = 2048;
int newValue = 1000;
VarHandle intMemoryHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
try (MemorySegment accessedSegment = MemorySegment.allocateNative(allocatedBlockSize)) {
MemoryAddress addressBase = accessedSegment.baseAddress();
System.out.println("Base Address: " + addressBase);
System.out.println("Old value of accessed memory: " + intMemoryHandle.get(addressBase));
System.out.println("Set new value " + newValue + " into the accessed memory.");
intMemoryHandle.set(addressBase, newValue);
System.out.println("New value of accessed memory: " + intMemoryHandle.get(addressBase));
}
}
}
"MemoryHandles.varHandle" creates a handle to edit and get the values of an Integer of a certain memory location. "ByteOrder.nativeOrder()" tells us to use the Byte order from the RAM (memory) of the operating system. An object "VarHandle" is returned, which has also the following methods "set" to set a certain value at a memory address and "get" to get a value from a certain memory address.
A memory segment is created with the function "MemorySegment.allocateNative", which will be called in a "try-with-resources" block to ensure that the created memory segment is released at the end of the program code.
You can get the base memory address of a certain memory segment with the method "baseAddress()".
Documentation:
https://download.java.net/java/GA/jdk14/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/package-summary.html
More about this new Foreign-Memory Access API:
https://openjdk.java.net/jeps/383