Seit Java 9 hat jede neue Java-Version Inkubator-Module, die noch in Entwicklung befindliche Funktionen enthalten und von Entwicklern getestet werden können.
Einer dieser Inkubatoren ist die Foreign-Memory Access API (JEP 370), die in diesem Tutorial vorgestellt wird.
Auf ein Speichersegment kann über eine Speicheradresse zugegriffen werden, die vorher überprüft wird, ob sie Teil der Grenze des von Ihnen referenzierten Speichersegments ist. Wenn Sie auf eine nicht geprüfte Speicheradresse zugreifen wollen und diese Teil eines Speichersegments war, das der Client hatte, dann müssen Sie den Offset der nicht geprüften Adresse relativ zur Basisadresse des Segments neu interpretieren (Rebase-Operation: MemoryAddress::rebase). Wenn ein solches Speichersegment nicht vorhanden ist, können Sie stattdessen die Factory-Klasse "MemorySegment::ofNativeRestricted" verwenden. Diese Factory kann nur verwendet werden, wenn die JDK-Eigenschaft "foreign.restricted" nicht auf den Wert "permit" gesetzt ist, und sie sollte mit Vorsicht verwendet werden.
Nur ein Thread kann jeweils nur auf ein bestimmtes Speichersegment zugreifen (und Eigentümer eines bestimmten Speichersegments sein), was "zeitliche Sicherheit" (temporal safety) gewährleistet.
Ein anderer Thread erhält Zugriff auf ein besetztes Speichersegment, nachdem der Inhaber (Besitzer) des Speichersegments das Segment geschlossen hat. Wenn Sie parallel auf Speicheradressen zugreifen und diese bearbeiten wollen, dann können Sie einen "Spliterator" verwenden, der ein Speichersegment in Scheiben aufteilt. Jede Scheibe wird dann von bestimmten Threads verwendet, um Teile der Gesamtaufgabe auszuführen. Das Speichersegment kann während dieses Prozesses nicht geschlossen werden, um die Sicherheit zu erhöhen.
Diese API stellt dies sicher:
- Sie kann mit verschiedenen Arten von fremdem Speicher arbeiten (Beispiel: persistenter Speicher, verwalteter Heap-Speicher usw.)
- Sie untergräbt nicht die Sicherheit der "Java Runtime Machine"
- Zuweisungen werden explizit in Ihrem Java-Code vorgenommen
- Größerer Fokus auf die Benutzerfreundlichkeit im Vergleich zu anderen Alternativen (Beispiel: "ByteBuffer API")
Wenn Sie Ihre Anwendung mit dieser API kompilieren möchten, dann müssen Sie das Modul "jdk.incubator.foreign" hinzufügen.
Beispiel (Wenn Sie die Befehlszeile verwenden):
javac --add-modules jdk.incubator.foreign ForeignMemoryAccess.java
Eine Beispielanwendung mit der 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" erzeugt ein Handle zum Bearbeiten und Abrufen der Werte einer ganzen Zahl einer bestimmten Speicherstelle. "ByteOrder.nativeOrder()" weist dem Handle an, die Byte-Reihenfolge aus dem RAM (Speicher) des Betriebssystems zu verwenden. Es wird ein Objekt "VarHandle" zurückgegeben, das auch die folgenden Methoden "set" hat, um einen bestimmten Wert an einer Speicheradresse zu setzen und "get", um einen Wert von einer bestimmten Speicheradresse zu erhalten.
Ein Speichersegment wird mit der Funktion "MemorySegment.allocateNative" erzeugt, die in einem "try-with-resources"-Block aufgerufen wird, um sicherzustellen, dass das erzeugte Speichersegment am Ende des Programmcodes freigegeben wird.
Die Basis-Speicheradresse eines bestimmten Speichersegments können Sie mit der Methode "baseAddress()" ermitteln.
Dokumentation:
https://download.java.net/java/GA/jdk14/docs/api/jdk.incubator.foreign/jdk/incubator/foreign/package-summary.html
Mehr über diese neue Foreign-Memory-Access-API:
https://openjdk.java.net/jeps/383