2009-02-17 21 views
33

Wenn wir uns die Java Object Klasse anschauen, dann können wir einige der folgenden Methoden finden:Was ist eine native Implementierung in Java?

public native int hashCode() 
protected native Object clone() 

Was sind diese Eingeborenen und wie funktionieren diese Methoden?

Antwort

34

Diese Methoden sind entweder Intrinsic oder geschrieben außerhalb von Java in "nativem" Code, dh spezifisch für die angegebene Maschine.

Die, die Sie erwähnen sind Intrinsic und ein Teil des JDK aber Sie können auch native Methoden, sich mit der Java Native Interface (JNI) schreiben. Dies würde normalerweise C verwenden, um die Methoden zu schreiben, aber viele andere Sprachen, wie Python, erlauben es Ihnen, Methoden auf diese Weise ziemlich einfach zu schreiben. Code wird auf diese Weise entweder für die Leistung geschrieben oder weil er auf eine plattformspezifische Infrastruktur zugreifen muss, die in Plain-Java nicht möglich ist.

Im Fall hashcode() wird dies von der JVM implementiert. Dies liegt daran, dass der Hashcode oft mit etwas in Verbindung gebracht wird, das nur die JVM kennt. Bei frühen JVMs war dies mit der Position des Objekts im Speicher verknüpft - bei anderen JVMs kann sich das Objekt im Speicher bewegen, und so kann ein komplizierteres (aber immer noch sehr schnelles) Schema verwendet werden.

+6

Wenn hashcode() nur von JVM implementiert wird, warum muss es * nativ * sein? Was genau meinst du mit * intrinsisch * hier? – Geek

11

Native Methoden werden meist in C implementiert und in nativen Code kompiliert, der direkt auf der Maschine ausgeführt wird. Dies steht im Gegensatz zu normalen Methoden, die in Java implementiert und nach Java-Byte-Code kompiliert werden, der von der Java Virtual Machine (JVM) ausgeführt wird.

Um mit diesen Methoden von Java zu verbinden, müssen Sie Java Native Interface (JNI) verwenden.

Nativer Code wird hauptsächlich für den Zugriff auf Low-Level-Daten benötigt. Im Fall von hashCode ist dies die Adresse des Objekts im Speicher. Meine Schätzung für Klon ist, dass es den rohen Speicher von einem Give-Objekt auf den geklonten kopiert. Andere Verwendung von systemeigenem Code ist für den Zugriff auf Betriebssystemfunktionen oder -hardware.

Der Nachteil der Verwendung von nativem Code besteht darin, dass Sie die Sicherheit der JVM verlieren, d. H. Ihr Programm stürzt möglicherweise ab oder weist Sicherheitslücken aufgrund von Fehlern im nativen Code auf.

17

Die meisten nativen Methoden werden mit JNI implementiert, wie in anderen Antworten erwähnt.

Allerdings werden für die Performance kritische Methoden wie Object.hashCode typischerweise als intrinsische Methoden implementiert. Wenn der Bytecode in Maschinencode kompiliert wird, erkennt der Java-Compiler den Methodenaufruf und inline den entsprechenden Code direkt ein. Dies wird offensichtlich viel schneller sein, als JNI für eine triviale Methode zu durchlaufen.

Viele Leute behaupten, dass Object.hashCode die Adresse der Objektdarstellung im Speicher zurückgibt. In modernen Implementierungen bewegen sich Objekte tatsächlich innerhalb des Speichers. Stattdessen wird ein Bereich des Objektheaders verwendet, um den Wert zu speichern, der von der Speicheradresse zu der Zeit, zu der der Wert zum ersten Mal angefordert wird, abgeleitet werden kann.

+2

Für weitere Details [besuchen] (http://stackoverflow.com/a/13860488/390695) –

10

Was sind diese Eingeborenen und wie funktionieren diese Methoden?

Minimal Beispiel Dinge klarer zu machen:

Main.java:

public class Main { 
    public native int square(int i); 
    public static void main(String[] args) { 
     System.loadLibrary("Main"); 
     System.out.println(new Main().square(2)); 
    } 
} 

Main.c:

#include <jni.h> 
#include "Main.h" 

JNIEXPORT jint JNICALL Java_Main_square(
    JNIEnv *env, jobject obj, jint i) { 
    return i * i; 
} 

Kompilieren und Ausführen:

sudo apt-get install build-essential openjdk-7-jdk 
export JAVA_HOME='/usr/lib/jvm/java-7-openjdk-amd64' 
javac Main.java 
javah -jni Main 
gcc -shared -fpic -o libMain.so -I${JAVA_HOME}/include \ 
    -I${JAVA_HOME}/include/linux Main.c 
java -Djava.library.path=. Main 

Ausgang:

4 

Getestet auf Ubuntu 14.04. Arbeitete auch mit Oracle JDK 1.8.0_45.

Example on GitHub für Sie mit zu spielen.

Interpretation:

Es ermöglicht Ihnen:

  • Aufruf eine kompilierte dynamisch geladene Bibliothek (hier in C geschrieben) mit beliebigen Assembler-Code von Java
  • und die Ergebnisse zurück in Java

Dies könnte verwendet werden:

  • schreiben schnellen Code auf einem kritischen Abschnitt mit einem besseren CPU Montageanleitung (nicht CPU tragbar)
  • direkte Systemaufrufe machen (nicht OS portable)

mit dem Kompromiss der unteren Portabilität.

Es ist auch möglich, dass Sie Java von C zu nennen, aber Sie müssen zuerst eine JVM in C: How to call Java functions from C++?

Beispiel in der OpenJDK 8

finden lassen, wo Object#clone definiert in jdk8u60-b27.

Zuerst finden wir:

find . -name Object.java 

, die uns zu jdk/src/share/classes/java/lang/Object.java#l212 führt:

protected native Object clone() throws CloneNotSupportedException; 

Jetzt kommt der schwierige Teil, zu finden, wo Klon inmitten all der Indirektheit ist. Die Abfrage, die mir geholfen haben war:

find . -iname object.c 

, die entweder C oder C++ Dateien finden würden, die Object nativen Methoden implementieren könnte.Sie führt uns zu jdk/share/native/java/lang/Object.c#l47:

static JNINativeMethod methods[] = { 
    ... 
    {"clone",  "()Ljava/lang/Object;", (void *)&JVM_Clone}, 
}; 

JNIEXPORT void JNICALL 
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls) 
{ 
    (*env)->RegisterNatives(env, cls, 
          methods, sizeof(methods)/sizeof(methods[0])); 
} 

die uns zum JVM_Clone Symbol führt:

grep -R JVM_Clone 

, die uns zu hotspot/src/share/vm/prims/jvm.cpp#l580 führt:

JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle)) 
    JVMWrapper("JVM_Clone"); 

Nach ein paar Makros erweitert, wir kommen zu dem Schluss, dass dies der Definitionspunkt ist.

Verwandte Themen