JNI - Java Native Interface SYSTEM SOFTWARE 1 Probleme bei nativem Code in Java Methodenaufruf Wie kann man native Methoden aus Java-Code aufrufen? Wie kann man Java-Methoden aus nativem Code aufrufen? Parameterübergabe und Rückgabewärte Wie werden Parameterwerte und Rückgabewerte übergeben Zugriff auf Java Memory Wie kann man Daten in einem Java-Objekt oder Array lesen und schreiben? Wie kann man ein Java-Objekt anlegen? Garbage Collection Wann kann ein Objekt freigegeben werden, das von nativem Code benutzt wird? SYSTEM SOFTWARE 2 Beispiel: Native Methoden in Java-Code Class Out definiert native Methode für Ausgabe Implementierung erfolgt in einer externen Bibliothek Laden der Library mit der nativen Implementierung vor dem ersten Aufruf Lädt Library mit Implementierung nativer Methoden class Out { static { System.loadLibrary("libout"); } public static native void print(String text); } SYSTEM SOFTWARE Methode mit Keyword native (wie abstract ,d.h. keine Implementierung) 3 Workflow Native Methoden in Java deklarieren (in Out.java) public static native void print(String text); Java Klassen kompilieren (erzeugt Out.class) javac Out.java Header Dateien generieren (erzeugt Out.h) javah Out Header Dateien inkludieren und native Methoden implementieren (in out.c) #include "Out.h" JNIEXPORT void JNICALL Java_Out_print(JNIEnv* env, jclass clazz, jstring text) {...} Nativen Code compilieren (erzeugt libout.so) Windows: libout.dll gcc -shared -fPIC -I<path-to-jdk-install-dir>/include -o libout.so out.c Programmstart mit Angabe des Pfads zur nativen Library java -Djava.library.path=. Out Native Library vor dem ersten Aufruf einer native Methode laden (in Out.java) static { System.loadLibrary("libout"); } SYSTEM SOFTWARE 4 JNI Headers javah generiert Header-Dateien aus kompilierten Java-Dateien /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class Out */ #ifndef _Included_Out #define _Included_Out Pro native Methode in Java gibt es eine Methode #ifdef __cplusplus im Header mit Namen: extern "C" { Java_<Klassenname>_<Methodenname> #endif /* * Class: Out * Method: print * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_Out_print (JNIEnv*, jclass, jstring); #ifdef __cplusplus } #endif #endif Macro für Library-Export Macro für C++ Calling conventions Parameter der Methode: Parameter werden in native Typen übersetzt JNI Environment: Schnittstelle zur Virtuellen Maschine Klasse der Methode (bei nichtstatischen Methoden jobject (this) SYSTEM SOFTWARE 5 JNI Type-Mapping Java Typ JNI Typ 32-bit Typ 64-bit Typ Signatur void void void void V byte jbyte signed char signed char B short jshort short short S int jint int int I long jlong long long long J float jfloat float float F double jdouble double double D boolean jboolean unsigned char unsigned char Z char jchar unsigned short unsigned short C java.lang.Object jobject * * Ljava/lang/Object; java.lang.String jstring * * Ljava/lang/String; java.lang.Class jclass * * Ljava/lang/Class; * * Ljava/lang/Throwable; java.lang.Object[] jobjectArray * * [Ljava/lang/Object; int[] jintArray * * [I ? jobject * * L<full-class-name>; java.lang.Throwable jthrowable SYSTEM SOFTWARE 6 Weitere JNI-Typen JNI Typ Verwendung jsize Array Längen jweak Weak References jvalue Basis für alle primitiven Typen jfieldID ID für Felder jmethodID ID für Methoden JNIEnv Interface zur Java VM Typen: Typen sind definiert in jni.h Geänderte Semantik von Typen, z.B.: Precision von float und double gleiche oder größere Wertebereiche kein Java-Overflow tatsächliche Typen sind architekturabhängig! Vererbungshierarchien gleich zu Java, z.B. jstring erbt von jobject SYSTEM SOFTWARE 7 Object Handles Jedes Objekt (jobject, jstring, …) ist nur ein Handle für ein Java-Objekt Nativer Code bekommt nie Pointer in Java Memory 3 Arten von Handles mit unterschiedlichen Lebenszeiten local lebt bis der native Frame zerstört wird, in dem der Handle angelegt wurde Parameter von nativen Methoden sind immer local kann explizit mit (*env)->DeleteLocalRef(env, obj) zerstört werden global lebt über native Frame-Grenzen hinweg muss explizit erzeugt und zerstört werden (NewGlobalRef, DeleteGlobalRef) weak werden explizit erzeugt und zerstört (NewGlobalRef, DeleteGlobalRef) Lebenszeiten wie global aber wie WeakReferece Objekt vom Garbage Collector freigegeben, wenn keine Referenzen in Java VM existieren Handle wird dann null SYSTEM SOFTWARE 8 Beispiel Object Handle void Java_Foo_bar(JNIEnv* env, jclass clazz, jobject obj) { static jobject last_obj = NULL; if(last_obj != NULL) { (*env)->DeleteGlobalRef(env, last_obj); } last_obj = (*env)->NewGlobalRef(env, obj); printf(“obj %s last_obj\n”, obj == last_obj ? “==” : “!=”); printf(“obj is %s the same as last_obj\n”, (*env)->IsSameObject(obj, last_obj) ? “indeed” : “not”); } Output: obj != last_obj obj is indeed the same as last_obj SYSTEM SOFTWARE 9 Zugriff auf Java Memory: Felder Suche Klasse jclass GetObjectClass(JNIEnv*, jobject obj) jclass FindClass(JNIEnv*, char* name) Ähnlich zu Reflection Suche Feld (mit Klasse und Name) jfieldID GetFieldID(JNIEnv*, jclass clazz, char* name, char* sig) Lese / schreibe Feld <T> Get<T>Field(JNIEnv*, jobject obj, jfieldID field); void Set<T>Field(JNIEnv*, jobject obj, jfieldID field, <T> value); <T> … für unterschiedliche Typen JNIEnv* env = ... jobject person = ... jclass clazz = (*env)->GetObjectClass(env, person); jfieldID field = (*env)->GetFieldID(env, clazz, "age", "I"); jint age = (*env)->GetIntField(env, person, field); (*env)->SetIntField(env, person, field, age + 1); (*env)->DeleteLocalRef(clazz); SYSTEM SOFTWARE 10 Zugriff auf Java Memory: Array <T> … für unterschiedliche Typen 3 Zugriffsarten Elementweiser Zugriff <T> Get<T>ArrayElement(JNIEnv*, j<T>Array array, jsize index) void set<T>ArrayElement(JNIEnv*, j<T>Array array, jsize index, <T> value) Regionsweiser Zugriff Get<T>ArrayRegion(JNIEnv*, j<T>Array array, jsize start, jsize length, <T>* buffer) Set<T>ArrayRegion(JNIEnv*, j<T>Array array, jsize start, jsize length, <T>* buffer) Arrayweiser Zugriff <T>* get<T>ArrayElements(JNIEnv*, j<T>Array array) void Release<T>ArrayElements(JNIEnv*, j<T>Array array, <T>* elems, jint mode) 0 kopiere Elemente in array und gib Buffer frei JNI_COMMIT kopiere Elemente in array und gib Buffer nicht frei JNI_ABORT kopiere Elemente nicht zurück und gib Buffer frei SYSTEM SOFTWARE 11 Beispiele: Zugriff auf Arrays JNIEnv* env = ...; jintArray array = …; //Access each element individually for(jsize i = 0; i < (*env)->GetArrayLength(env, array); i++) { jint value = (*env)->GetIntArrayElement(env, array, i); (*env)->SetIntArrayElement(env, array, i, value + 1); } //Access the entire array at once jint* native_array = (*env)->GetIntArrayElements(env, array); for(jsize i = 0; i < (*env)->GetArrayLength(env, array); i++) { native_array[i]++; } (*env)->ReleaseIntArrayElements(env, array, native_array, 0); //Access chunks of the array jint* chunk = (jint*) calloc(16, sizeof(jint); for(jsize i = 0; i < (*env)->GetArrayLength(env, array) / 16; i++) { (*env)->GetIntArrayRegion(env, array, i*16, 16, chunk); for(int i = 0; i < 16; i++) { chunk[i]++; } (*env)->SetIntArrayRegion(env, array, i*16, 16, chunk); } free(chunk); SYSTEM SOFTWARE 12 Zugriff auf Java Memory: Strings Inhalt wird nicht zurückgeschrieben! Lesender Zugriff wie auf Arrays jsize GetStringLength(JNIEnv*, jstring string) jchar* GetStringChars(JNIEnv*, jstring string) void ReleaseStringChars(JNIEnv*, jstring string, jchar* chars) JNIEXPORT void JNICALL Java_Out_print(JNIEnv* env, jclass clazz, jstring text) { if(text != NULL) { jsize length = (*env)->GetStringLength(env, text); jchar* characters = (*env)->GetStringChars(env, text); char* native_characters = calloc(length + 1, sizeof(char)); for(jsize i = 0; i < length; i++) { native_characters[i] = (char) characters[i]; //assume ASCII only } native_characters[length] = '\0'; (*env)->ReleaseStringChars(env, text, characters); printf("%s", native_characters); free(native_characters); } } clazz und text sind local Handles und müssen deswegen nicht explizit gelöscht werden SYSTEM SOFTWARE 13 Aufrufe von Java aus Native-Code Suche Klasse wie bei Feldzugriff "(" ParamSig0 ParamSig1 … ")" ReturnSig zB: "(JJ)Z" Suche Methode jmethodID GetMethodID(JNIEnv*, jclass clazz, char* name, char* sig) Parameters Aufruf <T> Call<T>Method(JNIEnv*, jobject thiz, jmethodID method, ...) <T> CallNonvirtual<T>Method(JNIEnv*, jobject thiz, jclass clazz, jmethodID method, ...) <T> CallStatic<T>Method(JNIEnv*, jclass clazz, jmethodID method, ...) Spezielle Funktionen zur Objekterzeugung jobject NewObject(JNIEnv*, jclass clazz, jmethodID constructor, ...) jstring NewString(JNIEnv*, jchar* chars, jsize length) j<T>Array New<Type>Array(JNIEnv*, jsize length) jobject AllocObject(JNIEnv*, jclass clazz) SYSTEM SOFTWARE Erzeugt Objekt ohne Konstruktor aufzurufen! 14 Beispiel: Aufrufe von Java aus Native-Code JNIEnv* env = ...; jobject person1 = ...; jobject person2 = ...; jclass object_class = (*env)->FindClass(env, " Ljava/lang/Object"); (Object): boolean const char* sig = "(Ljava/lang/Object;)Z"; jmethodID equals_method = (*env)->GetMethodID(env, object_class, "equals", sig); jboolean equals = (*env)->CallBooleanMethod(env, person1, equals_method, person2); (*env)->DeleteLocalRef(env, object_class); boolean equals(Object) SYSTEM SOFTWARE 15 Exceptions JNIEXPORT void JNICALL Java_Person_raiseSalary (JNIEnv* env, jclass clazz, jobject person, jdouble factor) { if(person == NULL) { char* exception_name = "java/lang/NullPointerException"; jclass exception_class = (*env)->FindClass(env, exception_name); (*env)->ThrowNew(env, exception_class, “person must not be null”); return; } ... } ThrowNew wirft Exception in Java VM erst, wenn die native Methode beendet ist! SYSTEM SOFTWARE 16 Weitere JNI-Methoden Methode Beschreibung ExceptionOccurred Prüft, ob der zuletzt ausgeführte Aufruf eine Exception ausgelöst hat ExceptionDescribe Erzeugt einen String, der die Exception beschreibt IsSameObject Prüft Objekte auf Referenzgleichheit IsInstanceOf Prüft, ob ein Objekt von einem bestimmten Typ ist http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html SYSTEM SOFTWARE 17 Zusammenfassung JNI ermöglich native Code in Java einzubinden Für jeden Java-Typ gibt es einen nativen JNI-Typ Nativer Code kann Java-Objekte benutzen und Java-Methoden aufrufen Nativer Code bekommt nie einen direkten Pointer auf Java Memory Manuelle Speicherverwaltung bei Java-Objekten (Gefahr von Memory Leaks!) SYSTEM SOFTWARE 18