2016-05-09 4 views
1

Ich möchte die Adressen von Java-Objekten (insbesondere Byte-Arrays) an Systemaufrufe und externe Funktionen übergeben, die innerhalb eines JNI-Aufrufs zurückgeben.Ist direkter Puffer für JNI/externe Bibliotheksaufrufe erforderlich?

Die Antwort von OpenJDK eigenen JNI-Funktionen ist offenbar keine - zum Beispiel der readbytes/writebytes io_util von Datei-Streams verwendete immer einen direkten Puffer/Heap für externe Anrufe im Zusammenhang etwas von malloc einzuführen.

Aber warum? Ich überprüfte den Code in java.nio.Bits (copyFromArray) und sun.misc.Unsafe (copyMemory), und es ist sehr klar, dass der Inhalt von primitiven Java-Arrays direkt in gewöhnlichem C-Code ohne irgendeine spezielle Behandlung (wie das Benachrichtigen) zugegriffen werden kann VM für Memory-Pinning oder Umgang mit nicht-kontinuierlichen Blöcken), solange es sich im Rahmen der aktuellen JNI-Funktion befindet. Es scheint also GC kann nicht passieren, wenn eine JNI-Methode aufgerufen wird, aber wenn das stimmt, warum readBytes/writeBytes immer eine Kopie der Daten in C-Heap erstellen?

Jeder mit Erfahrung darin? Ich suche keinen offiziellen Rat/Empfehlung von OpenJDK oder Oracle. Kein Interesse an Portabilität oder irgendetwas jenseits der aktuellen Implementierungen.

+0

mehrere einfache Tests zeigen, es funktioniert gut, aber ich habe immer noch keine Ahnung, ob es irgendeine Nebenwirkung wäre. – AqD

Antwort

2

Das Pinning des Arrays mit GetPrimitiveArrayBytesCritical() ist sehr leicht und wurde genau für diesen Zweck gedacht. Aber JNI-Spezifikation beschreibt explizit, was Aktionen zwischen Get/Veröffentlichung ... Critical durchgeführt werden:

Nach GetPrimitiveArrayCritical Aufruf, der native Code soll nicht über einen längeren Zeitraum laufen, bevor es ReleasePrimitiveArrayCritical nennt. Wir müssen den Code in diesem Funktionspaar so behandeln, dass er in einer "kritischen Region" läuft. In einer kritischen Region darf systemeigener Code keine anderen JNI-Funktionen oder einen Systemaufruf aufrufen, durch den der aktuelle Thread blockiert und auf einen anderen Java-Thread gewartet wird. (Zum Beispiel muss der aktuelle Thread nicht auf einem Strom von einem anderen Java-Thread geschrieben wird gelesen nennen.)

Auch erklärt die doc, dass eine JVM intern Arrays in einem anderen Format darstellen könnte, und dann auch diese Funktionen wird eine Kopie zurückgeben. DirectByteBuffer dagegen ist garantiert in C-kompatiblem Format.

Die Nebenwirkungen, die Ihre einfachen Experimente nicht erklären konnten, sind Ausnahmen, die entweder in Java oder in C ausgelöst werden, Zugriff von anderen Threads und natürlich - alternative JVM. Die Spezifikationen wurden festgelegt, bevor die 64-Bit-Adressierung des virtuellen Speichers weit verbreitet wurde, und jetzt gibt es keinen Grund mehr, sie zu ändern.

Alle JNI ist seit vielen Jahren eine Art eingefroren, weil es einerseits "gut genug" und andererseits "nicht wichtig genug" ist, und das Hauptanliegen ist Kompatibilität, die eine ernsthafte Herausforderung an sich ist, die Anzahl der unabhängigen Implementierungen gegeben.

+0

Aber der Code von unsafe.cpp in OpenJDK zeigt an, dass es das Array nicht anheften oder irgendetwas anderes tun muss. Es wird bereits intern für primitive Arrays ohne Größenbeschränkung verwendet und greift direkt auf die Elemente durch den Zeiger auf das primitive Array zu. In Bezug auf das Problem syscall/blocking führt JVM GC aus, während Threads für I/O blockiert sind? Aber wie unterscheidet es langsames IO und schnelle Speicheroperationen (wie in unsafe.cpp) innerhalb JNI? – AqD

+0

Aber OpenJDK ist eine und nicht die beliebteste Implementierung. –

+0

Auch wenn ich Get/Release verwende, ist es in OpenJDK praktisch nutzlos, da kein Speicher angeheftet ist und ich immer noch Puffer für lese()/write() auf Socket und accept() kopieren muss, ansonsten GC den Speicher verschieben oder freigeben Get gibt nur die aktuelle Speicheradresse eines Objekts zurück. Wie ist die kritische Region definiert?Wie kann ich sicher sein, dass einige Operationen in einer kritischen Region sein können oder nicht? (Ich übergebe auch direkten Java-Puffer zu getdents() und readlink() etc) – AqD

Verwandte Themen