1 / 31

Java Native Interface

Java Native Interface. Eine Einführung. Motivation. Java: abstrahiert von Hardware und Betriebssystem + hohe Portabilität + hohe Sicherheit ( Sandbox -Prinzip) - verminderte Performanz (mit heutigen JIT-Compilern i.A. kein Problem mehr)

wiley
Download Presentation

Java Native Interface

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Java Native Interface Eine Einführung

  2. Motivation • Java: abstrahiert von Hardware und Betriebssystem + hohe Portabilität + hohe Sicherheit (Sandbox-Prinzip) - verminderte Performanz (mit heutigen JIT-Compilern i.A. kein Problem mehr) - kein direkter Zugriff auf Hardware-/Betriebssystem • C/C++: Programmiersprache für OS-Entwicklung (UNIX) + maschinennah, hohe Performanz + alt, weit verbreitet, umfangreiche Sammlung von Legacy-Code - Programmierung möglicherweise umständlich (GUI in C?) - hohe Anforderungen an Programmierer

  3. Das Java Native Interface (JNI) • Schnittstelle, um C/C++ -Code in Java einzubinden und umgekehrt • Beispiele - C/C++ in Java: • Treiber, Betriebssystemfunktionen ansprechen (SWT) • Vorhandene Bibliotheken einbinden • Zeitkritische Funktionen in C oder Assembler einbinden  Möglicherweise Verlust der Portabilität! • Beispiel – Java in C/C++: • Browser, der Applets ausführt

  4. JNI - Architektur • Schnittstelle definiert Datentypen und Funktionen… • zum Aufruf nativer Funktionen in Java • zur Abbildung von Java-Datentypen in C/C++ • zur Erzeugung und Mani- pulation von Java-Objekten in C/C++ (OO in C?) • zum Aufruf einer JVM aus C/C++ heraus

  5. C in Java : Hallo Welt • Minimal-Beispiel: Aufruf einer nativen Methode, die „Hallo Welt!“ auf der Konsole ausgibt. • Prinzipielles Vorgehen (immer gleich):

  6. Hallo Welt – Java-Code • Definition einer nativen Methode ohne Rumpf • Dynamisches Laden der Bibliothek, die Methode bereitstellt • Kompilieren mit: javac HelloWorld.java classHelloWorld { private nativevoidprint(); public static void main(String[] args) { newHelloWorld().print(); } static { System.loadLibrary("HelloWorld"); } }

  7. Hallo Welt – C/C++-Header • Die C-Methode, die in HelloWorld aufgerufen wird, muss gewisse Konventionen erfüllen • javah –jniHelloWorld.classerzeugt Header-Datei HelloWorld.hmit Prototypen aller nativen Methoden: • Methoden-Name muss in C eindeutig sein, daher: Java_packageName_className_methodName • Parameterlose Funktion print() erhält in C zwei Parameter !? • include <jni.h> […] • JNIEXPORT void JNICALL • Java_HelloWorld_print (JNIEnv *env, jobject); […]

  8. Hallo Welt – C-Code • Implementierung der in Header-Datei definierten Funktion in Datei HelloWorld.c: • Übersetzen in gemeinsam genutzte Bibliothek gcc -fPIC -c HelloWorld.c ld -shared -sonamelibHelloWorld.so.l -o libHelloWorld.soHelloWorld.o • #include "HelloWorld.h" • #include <stdio.h> • JNIEXPORT void JNICALL • Java_HelloWorld_print (JNIEnv *env, jobjectobj){ • printf("Hallo Welt!\n"); • }

  9. Hallo Welt - Finale • Ausführen des Java-Programms: • javaHelloWorld • java.lang.UnsatisfiedLinkError: no HelloWorld in library path at java.lang.Runtime.loadLibrary(Runtime.java) at java.lang.System.loadLibrary(System.java) at HelloWorld.main(HelloWorld.java) Pfad zur Bibliothek noch unbekannt! Entweder • Bibliothek in /usr/lib verschieben (root-Rechte!), • setenv LD_LIBRARY_PATH .:${LD_LIBRARY_PATH}, • oder Bibliothekpfad explizit an Java übergeben: java –Djava.library.path=. HelloWorld

  10. Abbildung der Datentypen • Bisher: kein Datenaustausch zwischen Java und C • Datentypen in Java: Primitive Datentypen, Klassen, Objekte, Arrays • Unterschiede Java vs. C: • Java: Einheitliche Spezifikation primitiver Datentypen auf allen Plattformen C: plattformabhängig (z.B. 2Byte int, 4Byte int) • Java: Objektorientierung C: keine Objekte, aber strukturierte Datentypen und Funktionszeiger

  11. Abbildung primitiver Datentypen

  12. Abb. primitiver Datentypen – Beispiel • publicclassSum{ • static{ System.loadLibrary("Sum"); } • publicstatic native intsum(int a, int b); • publicstaticvoidmain(String[] args){ • System.out.println("sum(3,4)=" + sum(3,4)); • }} #include <jni.h> […] JNIEXPORT jint JNICALL Java_Sum_sum (JNIEnv *env, jclass, jint, jint); […] • JNIEXPORT jint JNICALL • Java_Sum_sum(JNIEnv *env, jclass cl, jint a, jint b){ • jintresult = a + b; • returnresult; • }

  13. Abbildung von Referenz-Datentypen • Objekte, Klassen und Arrays werden in Java über Referenzen angesprochen • An C wird ein „transparenter“ Pointer übergeben, der auf die Objekte in der JVM zeigt. • Zugriff auf die Objekte erfolgt immer über die JNIEnv-Umgebung, die jeder nativen Methode übergeben wird:

  14. Abb. von Referenztypen – z.B. Arrays importjava.util.Random; publicclassSort{ static{ System.loadLibrary("QSort"); } publicstatic native voidsort(double[] arr); publicstaticvoidmain(String[] args){ […] double[] arr = new double[Integer.parseInt(args[0])]; Random r = new Random(); for(int i=0; i< arr.length; arr[i++] = r.nextInt(10000)); long s = System.currentTimeMillis(); sort(arr); long e = System.currentTimeMillis(); System.out.println("Sortierung dauerte "+(e-s)+"ms"); } }

  15. Abb. von Referenztypen – z.B. Arrays • Prototyp: • Zugriff auf Array-Elemente: • JNIEXPORT void JNICALL • Java_Sort_sort (JNIEnv *env, jclass, jdoubleArray); • JNIEXPORT void JNICALL • Java_Sort_sort (JNIEnv *env, jclass cl, jdoubleArrayarr){ • jdouble *cArray = (jdouble*) arr; // FALSCH!!! • // arr ist lediglich ein Pointer auf eine JVM- • // Datenstruktur. Der Zugriff auf die Array-Elemente • // muss über die JNIEnv Umgebung erfolgen! • […] • }

  16. Abb. von Referenztypen – z.B. Arrays intcompare(constvoid *a, constvoid *b){ return *((jdouble*) a) < *((jdouble*) b) ? -1 : +1; } JNIEXPORT void JNICALL Java_Sort_sort (JNIEnv *env, jclass cl, jdoubleArrayarr){ jbooleanisCopy; jdouble* cArray = (*env)->GetDoubleArrayElements(env, arr, isCopy); jintlength = (*env)->GetArrayLength(env, array); qsort(cArray, length, sizeof(jdouble), &compare); if(isCopy) (*env)->ReleaseDoubleArrayElements(env, array, cArray, 0); }

  17. Array-Zugriffsmethoden • Get<Type>ArrayRegion, Set<Type>ArrayRegion • Kopiert Bereich aus oder in ein Array mit primitivem Datentyp • Get<Type>ArrayElements, Release<Type>ArrayElements • Erhält einen Pointer auf ein Array (ggf. auch Kopie) und gibt ihn wieder frei (ansonsten Speicherleck!) • GetArrayLength • Ermittelt die Länge eines Arrays • New<Type>Array • Legt ein neues Array an • Nur auf primitive Arrays anwendbar!

  18. Zugriff auf Objekt-Arrays • Beispiel: • String[] strArray; • int[][] intArray; • Object[] objArray; • Unmöglich, gesamtes Array abzufragen • Lediglich einzelne Einträge zugreifbar • GetObjectArrayElement • SetObjectArrayElement

  19. Beispiel: Anlegen eines 2D-Feldes • Zuerst: Anweisung an JVM, ein int[]array anzulegen: • private static native int[][] initInt2DArray(int size); JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclass c, int s){ jobjectArray result; inti; jclassintArrCls = (*env)->FindClass(env, "[I"); if (intArrCls == NULL) return NULL; /* exception thrown */ result = (*env)->NewObjectArray(env, size, intArrCls, NULL); if (result == NULL) return NULL; /* out of memory error thrown */ …

  20. Beispiel: Anlegen eines 2D-Feldes • Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen … for (i = 0; i < size; i++) { jinttmp[256]; /* make sure it is large enough! */ int j; jintArrayiarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; }

  21. Beispiel: Anlegen eines 2D-Feldes • Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen … for (i = 0; i < size; i++) { jinttmp[256]; /* make sure it is large enough! */ int j; jintArrayiarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } Inhalt von tmp nach iarr kopieren

  22. Beispiel: Anlegen eines 2D-Feldes • Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen … for (i = 0; i < size; i++) { jinttmp[256]; /* make sure it is large enough! */ int j; jintArrayiarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } Array an übergeordnetes Objekt-Array result übergeben

  23. Beispiel: Anlegen eines 2D-Feldes • Dann: Für jeden Eintrag des int[] Arrays ein primitives int[] Array anlegen … for (i = 0; i < size; i++) { jinttmp[256]; /* make sure it is large enough! */ int j; jintArrayiarr = (*env)->NewIntArray(env, size); if (iarr == NULL) return NULL; /* out of memory error */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp); (*env)->SetObjectArrayElement(env, result, i, iarr); (*env)->DeleteLocalRef(env, iarr); } return result; } Kontrolle über iarr an JVM geben

  24. Arbeiten mit Strings • Java Strings sind Unicode-Codiert, C-Strings sind ASCII-codiert • Anbindung über UTF-8 (kann beide Codierungen abbilden) • GetStringChars, ReleaseStringChars • GetStringUTFChars, ReleaseStringUTFChar • GetStringLength, GetStringUTFLength • NewString, NewStringUTF • GetStringRegion, SetStringRegion • GetStringUTFRegion, SetStringUTFRegion • Anbindung sehr umständlich, Java im Umgang mit Strings komfortabler, daher wenig Relevanz

  25. Zugriff auf Objekt-Felder und -Methoden • Bei komplexen Klassen-Instanzen kann aus C heraus auf die Felder und Methoden der Instanz zugegriffen werden. • Mechanismus erscheint relativ umständlich, daher hier nur exemplarisch dargestellt. Für nähere Infos siehe JNI-Referenz

  26. Zugriff auf Static-Feld • Java-Klasse mit statischem Feld si • si soll in nativer Methode manipuliert werden class StaticFieldAccess { private static intsi; private native void accessField(); public static void main(String args[]) { StaticFieldAccess c = new StaticFieldAccess(); StaticFieldAccess.si = 100; c.accessField(); System.out.println("In Java:"); System.out.println(" StaticFieldAccess.si = " + si); } static { System.loadLibrary("StaticFieldAccess"); } }

  27. Zugriff auf Static-Feld JNIEXPORT void JNICALL Java_StaticFieldAccess_accessField(JNIEnv *env, jobjectobj){ jfieldID fid; /* store the field ID */ jintsi; /* Get a reference to obj’s class */ jclasscls = (*env)->GetObjectClass(env, obj); /* Look for the static field si in cls */ fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); if (fid == NULL) return; /* field not found */ /* Access the static field si */ si = (*env)->GetStaticIntField(env, cls, fid); printf(" StaticFieldAccess.si = %d\n", si); (*env)->SetStaticIntField(env, cls, fid, 200); }

  28. Zugriff auf Instanz-Methode • Methode callback() soll in nativeMethod() gerufen werden • class InstanceMethodCall { • private native void nativeMethod(); • private void callback() { • System.out.println("In Java"); • } • public static void main(String args[]) { • InstanceMethodCall c = new InstanceMethodCall(); • c.nativeMethod(); • } • static { • System.loadLibrary("InstanceMethodCall"); • } • }

  29. Zugriff auf Instanz-Methode JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobjectobj){ jclasscls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V"); if (mid == NULL) return; /* method not found */ printf("In C\n"); (*env)->CallVoidMethod(env, obj, mid); }

  30. Das war erst der Anfang... • JNI bietet noch mehr: • Erzeugen von Objekten, Aufruf von Konstruktoren • Caching von Methoden-/Feld-Ids • Exception-Handling • … Diese Themen werden dem geneigten Hörer zum Selbststudium überlassen 

  31. Fazit • Enorme Möglichkeiten, positive Aspekte von C, C++ und Java zu integrieren • Aufwand allerdings nicht unerheblich, daher sollten immer auch Alternativen in Betracht gezogen werden • Für Standardaufgaben ist Einsatz von JNI aufgrund des stetig abnehmenden Performanz-Vorsprungs von C/C++ (wahrscheinlich) nicht lohnend

More Related