Ist es möglich, sun.misc.Unsafe zum Aufrufen von C-Funktionen ohne JNI zu verwenden?

Lesezeit: 5 Minuten

Benutzer-Avatar
FinnDerMensch

Ein Stück C/C++-Code könnte eine JNI-Methode mit einem Array von Funktionszeigern bereitstellen. Aber gibt es eine Möglichkeit, die Funktionen, auf die die Zeiger des Arrays zeigen, direkt aus dem Java-Code (ohne JNI oder ähnliches) auf den Stack aufzurufen? JNI macht so etwas irgendwie, also muss es einen Weg geben. Wie macht JNI das? Ist es über sun.misc.Unsafe? Selbst wenn dies nicht der Fall ist, könnten wir eine unsichere Problemumgehung verwenden, um den JVM-Code in die Hände zu bekommen, der dies tut?

Ich habe natürlich nicht vor, das kommerziell zu nutzen. Ich bin nicht einmal ein Profi, ich genieße das Codieren einfach sehr und habe in letzter Zeit CUDA studiert, also dachte ich, ich könnte vielleicht damit experimentieren, alles miteinander zu mischen, aber der Overhead von JNI-Aufrufen würde den Zweck von GPU-beschleunigtem Code zunichte machen.

  • JNI tut dies, indem es in die JVM integriert wird.

    – Benutzer253751

    30. März 2016 um 1:42 Uhr

Benutzer-Avatar
angin

Ist JNI so langsam?

JNI wurde bereits stark optimiert, Sie sollten es zuerst ausprobieren. Aber es hat in der Tat einen gewissen Overhead, siehe Details.

Dieser Overhead kann erheblich sein, wenn eine native Funktion einfach ist und häufig aufgerufen wird. JDK hat eine private API namens Kritische Eingeborene um den Overhead beim Aufrufen von Funktionen zu reduzieren, die nicht viel JNI-Funktionalität erfordern.

Kritische Eingeborene

Eine native Methode muss die folgenden Bedingungen erfüllen, um eine kritische native Methode zu werden:

  • muss sein statisch und nicht synchronisiert;
  • Argumenttypen müssen sein Primitive oder primitive Arrays;
  • die Implementierung darf keine JNI-Funktionen aufrufen, dh sie kann keine Java-Objekte zuweisen oder Ausnahmen auslösen;
  • sollte nicht lange laufen, da es blockiert GC während dem Rennen.

Abgesehen davon sieht die Deklaration eines kritischen Natives wie eine reguläre JNI-Methode aus

  • es beginnt mit JavaCritical_ Anstatt von Java_;
  • es hat nicht extra JNIEnv* und jclass Argumente;
  • Java-Arrays werden in zwei Argumenten übergeben: Das erste ist eine Array-Länge und das zweite ein Zeiger auf Array-Rohdaten. Das heißt, Sie müssen nicht anrufen GetArrayElements und Freunde, Sie können sofort einen direkten Array-Zeiger verwenden.

ZB eine JNI-Methode

JNIEXPORT jint JNICALL
Java_com_package_MyClass_nativeMethod(JNIEnv* env, jclass klass, jbyteArray array) {
    jboolean isCopy;
    jint length = (*env)->GetArrayLength(env, array);
    jbyte* buf = (*env)->GetByteArrayElements(env, array, &isCopy);
    jint result = process(buf, length);
    (*env)->ReleaseByteArrayElements(env, array, buf, JNI_ABORT);
    return result;    
}

wird sich wenden

JNIEXPORT jint JNICALL
JavaCritical_com_package_MyClass_nativeMethod(jint length, jbyte* buf) {
    return process(buf, length);
}

Kritische Natives werden nur in HotSpot JVM ab JDK 7 unterstützt. Darüber hinaus wird die „kritische“ Version nur aus kompiliertem Code aufgerufen. Daher benötigen Sie sowohl eine kritische als auch eine Standardimplementierung, damit dies ordnungsgemäß funktioniert.

Diese Funktion wurde für die interne Verwendung in JDK entwickelt. Es gibt keine öffentliche Spezifikation oder so etwas. Wahrscheinlich ist die einzige Dokumentation, die Sie finden können, in den Kommentaren zu JDK-7013347.

Benchmark

Dieser Maßstab zeigt, dass kritische Natives um ein Vielfaches schneller sein können als normale JNI-Methoden, wenn die native Arbeitslast sehr gering ist. Je länger die Methode ist, desto kleiner ist der relative Overhead.

Leistung von JNI-Aufrufen


PS Im JDK wird laufend daran gearbeitet, native MethodHandles zu implementieren, die als schnellere Alternative zu JNI dienen. Es ist jedoch unwahrscheinlich, dass es vor JDK 10 erscheint.

  1. http://cr.openjdk.java.net/~jrose/panama/native-call-primitive.html
  2. http://mail.openjdk.java.net/pipermail/panama-dev/2015-December/000225.html

  • Wie können Sie wissen, ob Ihr Programm die kritische Implementierung oder die Standardimplementierung verwendet? Ich habe gerade ein ähnliches Beispiel ausprobiert, mit dem ich meine Java-Datei kompiliert habe javac und lief es und es verwendete die nicht kritische Standardversion.

    – Ivaylo Toskov

    5. Mai 2017 um 12:26 Uhr

  • @IvayloToskov Nur JIT-kompilierte Methoden rufen Critical Natives auf. Während eine Methode interpretiert wird, ruft sie die Standardimplementierung auf.

    – angin

    5. Mai 2017 um 17:09 Uhr

  • Sollte die Methodendeklaration sowohl in der .c- als auch in der .h-Datei vorhanden sein?

    – Jaspreet

    27. Dezember 2018 um 7:14 Uhr

  • @Jaspreet Es ist nicht notwendig, es in .h zu haben

    – angin

    27. Dezember 2018 um 7:54 Uhr

  • Wird ein jbyteArray immer in einen Pointer und einen Längenwert zerlegt oder bleibt es auf Wunsch ein Jobject? Ich ziehe es vor, ein Jobject zu nehmen und dann auf die Zeiger zu verweisen, um festzustellen, ob es sich um ein Array oder einen langen Wert handelt. Gibt es eine Quelle in der JVM, auf die Sie verweisen können, um dieses Verhalten besser zu erklären?

    – Jonny V

    23. April 2019 um 14:55 Uhr

Es lohnt sich, das hier zu erwähnen eine weitere beliebte Open-Source-JVM hat ein ähnliches, dokumentiertaber keine populäre Möglichkeit, JNI-Aufrufe für einige native Methoden zu beschleunigen.

Schnellere native Aufrufe an das Java Native Interface (JNI) sind mit verfügbar @FastNative und @CriticalNative Anmerkungen. Diese integrierten ART-Laufzeitoptimierungen beschleunigen JNI-Übergänge und ersetzen die jetzt veraltete !bang-JNI-Notation. Die Anmerkungen haben keine Auswirkungen auf nicht-native Methoden und sind nur für Plattform-Java-Sprachcode im Bootclasspath verfügbar (keine Play Store-Updates).

Das @FastNative Annotation unterstützt nicht statische Methoden. Verwenden Sie dies, wenn eine Methode als Parameter oder Rückgabewert auf ein Jobobjekt zugreift.

Das @CriticalNative Annotation bietet eine noch schnellere Möglichkeit, native Methoden auszuführen, mit den folgenden Einschränkungen:

  • Methoden müssen statisch sein – keine Objekte für Parameter, Rückgabewerte oder ein implizites this.
  • Nur primitive Typen werden an die native Methode übergeben.
  • Die native Methode verwendet in ihrer Funktionsdefinition nicht die Parameter JNIEnv und jclass.
  • Die Methode muss bei RegisterNatives registriert werden, anstatt sich auf die dynamische JNI-Verknüpfung zu verlassen.

Das @FastNative und @CriticalNative Anmerkungen deaktivieren die Garbage Collection, während eine native Methode ausgeführt wird. Verwenden Sie es nicht mit lang andauernden Methoden, einschließlich normalerweise schneller, aber im Allgemeinen unbegrenzter Methoden.

Unterbrechungen der Garbage Collection können zu einem Deadlock führen. Erwerben Sie keine Sperren während eines schnellen nativen Aufrufs, wenn die Sperren nicht lokal freigegeben wurden (d. h. vor der Rückkehr zu verwaltetem Code). Dies gilt nicht für reguläre JNI-Aufrufe, da ART den ausführenden nativen Code als angehalten betrachtet.

@FastNative kann die Leistung nativer Methoden um das bis zu 3-fache verbessern und @CriticalNative bis zu 5x.

Dieses Dokument bezieht sich auf das jetzt veraltete !Knall Notation, die verwendet wurde, um einige native Aufrufe auf Dalvik JVM zu beschleunigen.

1344400cookie-checkIst es möglich, sun.misc.Unsafe zum Aufrufen von C-Funktionen ohne JNI zu verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy