35

如果我们查看 Java Object 类,那么我们可以找到一些方法,例如:

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

这些本地人是什么?这些方法是如何工作的?

4

5 回答 5

36

这些方法要么是内在的,要么是在 Java 之外以“本机”代码编写的,即特定于给定机器。

您提到的是Intrinsic和 JDK 的一部分,但您也可以使用Java 本机接口(JNI) 自己编写本机方法。这通常会使用 C 来编写方法,但许多其他语言,例如 python,允许您相当容易地以这种方式编写方法。以这种方式编写代码要么是为了提高性能,要么是因为它需要访问平台特定的基础设施,而这在纯 java 中是无法完成的。

在 的情况下hashcode(),这是由 JVM 实现的。这是因为哈希码通常与只有 JVM 知道的东西相关。在早期的 JVM 上,这与对象在内存中的位置有关——在其他 JVM 上,对象可能会在内存中移动,因此可以使用更复杂(但仍然非常快)的方案。

于 2009-02-17T16:21:56.710 回答
19

如其他答案中所述,大多数本机方法都是使用 JNI 实现的。

但是,性能关键方法(例如,Object.hashCode通常作为内在函数实现)。当字节码被编译成机器码时,Java 编译器会识别方法调用并直接内联适当的代码。这显然比通过 JNI 获得一个简单的方法要快得多。

许多人会声称Object.hashCode将返回内存中对象表示的地址。在现代实现中,对象实际上在内存中移动。取而代之的是,对象头的一个区域用于存储该值,该值可能是在首次请求该值时从内存地址中延迟派生的。

于 2009-02-19T13:57:59.657 回答
12

本机方法主要在 C 中实现,并编译为直接在机器上运行的本机代码。这与在 Java 中实现并编译为由 Java 虚拟机 (JVM) 执行的 Java 字节码的普通方法形成对比。

要从 Java 与这些方法交互,您需要使用Java Native Interface (JNI)

本机代码主要用于访问低级内容。在hashCode的情况下,这是内存中对象的地址。我对克隆的猜测是它将原始内存从给定对象复制到克隆对象。本机代码的其他用途是访问操作系统功能或硬件。

使用本机代码的缺点是您失去了 JVM 的安全性,即您的程序可能由于本机代码中的错误而崩溃或存在安全漏洞。

于 2009-02-17T16:21:00.347 回答
11

这些本地人是什么?这些方法是如何工作的?

使事情更清楚的最小示例:

主.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));
    }
}

主.c

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

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

编译并运行

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

输出

4

在 Ubuntu 14.04 上测试。还与 Oracle JDK 1.8.0_45 一起使用。

GitHub 上的示例供您使用。

解释

它允许您:

  • 使用来自 Java 的任意汇编代码调用已编译的动态加载库(此处用 C 编写)
  • 并将结果返回到 Java

这可用于:

  • 使用更好的 CPU 汇编指令在关键部分编写更快的代码(不是 CPU 可移植的)
  • 进行直接系统调用(不是操作系统可移植的)

以较低的便携性为代价。

您也可以从 C 调用 Java,但您必须先在 C 中创建一个 JVM:如何从 C++ 调用 Java 函数?

OpenJDK 8 中的示例

让我们找到Object#clone在jdk8u60-b27中定义的位置。

首先我们发现:

find . -name Object.java

这导致我们到jdk/src/share/classes/java/lang/Object.java#l212

protected native Object clone() throws CloneNotSupportedException;

现在是困难的部分,找到克隆在所有间接中的位置。帮助我的查询是:

find . -iname object.c

它将找到可能实现 Object 的本机方法的 C 或 C++ 文件。它引导我们到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]));
}

这将我们引向JVM_Clone符号:

grep -R JVM_Clone

这将我们带到了热点/src/share/vm/prims/jvm.cpp#l580

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

展开一堆宏后,我们得出结论,这就是定义点。

于 2015-06-04T06:02:58.573 回答
0

Java 中的本机方法是使用称为 JNI的“ Java 本机接口”实现的。

于 2009-02-17T16:17:11.713 回答