2009-02-17 38 views
33

Nếu chúng ta nhìn vào lớp Object Java thì chúng ta có thể tìm thấy một số phương pháp như:Triển khai gốc trong Java là gì?

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

những người bản địa là gì và làm thế nào để những phương pháp làm việc?

Trả lời

34

Các phương pháp này là Nội tại hoặc được viết bên ngoài Java bằng mã "gốc", nghĩa là, cụ thể cho máy đã cho.

Những cái bạn đề cập là Nội tại và một phần của JDK nhưng bạn cũng có thể tự viết các phương pháp gốc bằng cách sử dụng Java Native Interface (JNI). Điều này thường sẽ sử dụng C để viết các phương thức, nhưng rất nhiều ngôn ngữ khác, chẳng hạn như python cho phép bạn viết các phương thức theo cách này khá dễ dàng. Mã được viết theo cách này hoặc là cho hiệu suất, hoặc bởi vì nó cần phải truy cập cơ sở hạ tầng nền tảng cụ thể mà không thể được thực hiện trong java đơn giản.

Trong trường hợp hashcode(), điều này được thực hiện bởi JVM. Điều này là do thường hashcode sẽ liên quan đến một cái gì đó chỉ có JVM biết. Trên các JVM ban đầu, điều này liên quan đến vị trí của đối tượng trong bộ nhớ - trên các JVM khác, Object có thể di chuyển trong bộ nhớ, và do đó có thể sử dụng lược đồ phức tạp hơn (nhưng vẫn rất nhanh).

+6

nếu hashcode() được thực hiện bởi JVM chỉ tại sao nó yêu cầu phải là * gốc *? Chính xác bạn ngụ ý gì bởi * nội tại * ở đây? – Geek

11

Phương pháp gốc được triển khai chủ yếu trong C và được biên dịch thành mã gốc chạy trực tiếp trên máy. Điều này trái ngược với các phương thức thông thường, được thực hiện trong Java và được biên dịch sang mã byte Java, được thực thi bởi Java Virtual Machine (JVM).

Để giao tiếp với các phương thức này từ Java, bạn cần sử dụng Java Native Interface (JNI).

Mã gốc là chủ yếu cần thiết để truy cập nội dung cấp thấp. Trong trường hợp của hashCode đây là địa chỉ của đối tượng trong bộ nhớ. Đoán của tôi cho clone là nó sao chép bộ nhớ thô từ một đối tượng cung cấp cho một nhân bản vô tính. Các cách sử dụng mã gốc khác là để truy cập vào các tính năng hoặc phần cứng của hệ điều hành.

Hạn chế của việc sử dụng mã gốc là bạn mất an toàn và bảo mật của JVM, tức là chương trình của bạn có thể bị lỗi hoặc có lỗ hổng bảo mật do lỗi trong mã gốc.

17

Hầu hết các phương pháp gốc được triển khai bằng cách sử dụng JNI như được đề cập trong các câu trả lời khác.

Tuy nhiên, các phương pháp quan trọng hiệu suất như Object.hashCode thường được triển khai dưới dạng nội tại. Khi mã byte được biên dịch thành mã máy, trình biên dịch Java nhận ra lời gọi phương thức và inline mã thích hợp trực tiếp. Điều này rõ ràng sẽ nhanh hơn nhiều so với JNI cho một phương pháp tầm thường.

Nhiều người sẽ cho rằng Object.hashCode sẽ trả lại địa chỉ của đại diện đối tượng trong bộ nhớ. Trong các hiện thực hiện đại, các đối tượng thực sự di chuyển trong bộ nhớ. Thay vào đó, một khu vực của tiêu đề đối tượng được sử dụng để lưu trữ giá trị, có thể được bắt nguồn từ địa chỉ bộ nhớ tại thời điểm giá trị được yêu cầu đầu tiên.

+2

Để biết thêm chi tiết [truy cập] (http://stackoverflow.com/a/13860488/390695) –

10

Những người bản xứ này là gì và các phương pháp này hoạt động như thế nào?

Minimal dụ để làm cho mọi việc rõ ràng hơn:

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; 
} 

Biên dịch và chạy:

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 

Output:

4 

Thử nghiệm trên Ubuntu 14.04. Cũng làm việc với Oracle JDK 1.8.0_45.

Example on GitHub để bạn có thể chơi cùng.

Giải thích:

Nó cho phép bạn:

  • gọi một thư viện tự động nạp biên soạn (ở đây viết bằng C) với mã lắp ráp tùy ý từ Java
  • và nhận được kết quả trở lại vào Java

Điều này có thể được sử dụng để:

  • viết mã nhanh hơn trên một phần rất quan trọng với các hướng dẫn tốt hơn CPU lắp ráp (không CPU di động)
  • thực hiện cuộc gọi hệ thống trực tiếp (không phải hệ điều hành di động)

với sự cân bằng của tính di động thấp hơn.

Nó cũng có thể để bạn có thể gọi Java từ C, nhưng trước tiên bạn phải tạo một JVM trong C: How to call Java functions from C++?

Ví dụ trong OpenJDK 8

Hãy tìm thấy nơi Object#clone được định nghĩa trong jdk8u60-b27.

Đầu tiên chúng ta thấy:

find . -name Object.java 

đó đưa chúng ta đến jdk/src/share/classes/java/lang/Object.java#l212:

protected native Object clone() throws CloneNotSupportedException; 

Bây giờ đến phần cứng, tìm nơi clone là trong bối cảnh tất cả các gián tiếp. Truy vấn đã giúp tôi là:

find . -iname object.c 

sẽ tìm thấy tệp C hoặc C++ có thể triển khai phương pháp gốc của Object.Nó đưa chúng ta đến 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])); 
} 

dẫn chúng ta đến JVM_Clone biểu tượng:

grep -R JVM_Clone 

đó đưa chúng ta đến hotspot/src/share/vm/prims/jvm.cpp#l580:

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

Sau khi mở rộng một loạt các macro, chúng ta đến kết luận rằng đây là điểm định nghĩa.

Các vấn đề liên quan