Java中的原生关键字是什么?


Answers:


342

所述native关键字被应用于方法,以指示该方法是在本机代码使用JNI(Java本地接口)来实现。


3
实际的实现不必使用JNI。某些JRE方法由JVM内部处理。实际上,实现实际上不是本机代码甚至不是强制性的。它只是“用Java编程语言以外的其他语言实现”
霍尔格

443

最小的可运行示例

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

编译并运行:

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 AMD64上测试。还与Oracle JDK 1.8.0_45一起使用。

GitHub上的示例供您使用。

Java包/文件名中的下划线必须使用_1C函数名进行转义,如下所述:在包含下划线的Android包名中调用JNI函数

解释

native 允许您:

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

这可以用来:

  • 使用更好的CPU组装指令在关键部分上编写更快的代码(不便于CPU移植)
  • 进行直接系统调用(非OS便携式)

以降低便携性为代价。

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

Android NDK

除了必须使用Android样板进行设置外,该概念在此情况下完全相同。

官方的NDK存储库包含“规范”示例,例如hello-jni应用程序:

在您unzip.apk使用NDK Android上的O,你可以看到预编译.so对应下的本地代码lib/arm64-v8a/libnative-lib.so

TODO确认:此外,file /data/app/com.android.appname-*/oat/arm64/base.odex表示这是一个共享库,我认为这是AART预编译的.dex,它对应于ART中的Java文件,另请参见:什么是Android中的ODEX文件?那么也许Java实际上也可以通过native接口运行?

OpenJDK 8中的示例

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

我们将得出结论,它是通过native调用实现的。

首先我们发现:

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

这导致我们进入hotspot / src / share / vm / prims / jvm.cpp#l580

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

扩展了一堆宏之后,我们得出的结论是这是定义点。


1
极好的答案。只是一个脚注:对于static nativeJava方法,C ++函数的第二个参数的类型为jclass而不是jobject
SR_

@SR_感谢您提供信息。我的答案有误吗,还是只是一些额外的信息?
西罗Santilli郝海东冠状病六四事件法轮功

2
@Ciro对于那些以您的示例开头的人来说,这是一些额外的信息(SO上大约有300的答案可以作为参考)。我有一个函数,该函数的签名不正确,并且在堆栈上混乱,没有报告错误(在编译,链接或运行时)。因此,我认为在此步骤中要提请注意非常重要。
SR_

419

它标记了一种方法,它将以其他语言而不是Java来实现。它与JNI(Java本机接口)一起使用。

过去曾使用本机方法来编写性能至关重要的部分,但是随着Java变得越来越快,这种方法现在已不那么普遍了。当前需要本机方法

  • 您需要从Java调用用其他语言编写的库。

  • 您需要访问只能从其他语言(通常为C)访问的系统或硬件资源。实际上,许多与真实计算机交互的系统功能(例如磁盘和网络IO)只能这样做,因为它们调用了本机代码。

另请参见 Java本机接口规范


3
这是我的理解,我在Java文件中编写了System.currentTimeMillis()(本机),然后可以正常工作,JNI将调用用C或C ++或汇编语言编写的库或某些函数,然后将一些值返回到我的Java代码中。例如:这里的currentTimeMillis方法在JNI的帮助下调用了一个本机代码,并且该本机代码与系统资源进行了对话。例如:坐在主板上的计时器并因此获得返回值(系统时间)。请纠正我吗?
MKod 2014年

4
@MKod之类的方法currentTimeMillis是JDK的一部分,并带有注解,native因为实现是在JDK源代码本身中进行的。实现不太可能使用汇编语言。它可能会调用JVM在其上运行的操作系统的API方法。例如,在Windows上,它可能GetSystemTime在kernel32.dll中调用DLL方法。在另一个操作系统上,它将具有不同的实现。但是,当您使用native要编写的方法(与JDK方法相对)时,则必须使用JNI提供实现。
亚当·伯利

该语句对于Native关键字来说很重要……“您需要访问只能从其他语言(通常为C)访问的系统或硬件资源”。
atiqkhaled

@Kidburla我想问一下“实现在JDK源代码本身中”是什么意思吗?currentTimeMillis被标记为本机,java.lang.System因此它使用JNI,不是吗?
flow2k

1
@ flow2k是的,您所说的可能是正确的,我不确定为什么我在评论中(超过2年前)说过这一点
Adam Burley

59

直起Java语言规范

native用平台相关代码实现的方法,该代码通常以另一种编程语言(例如C,C ++,FORTRAN或汇编语言)编写。native方法的主体仅以分号表示,表示省略了实现,而不是块。


19

正如SLaks回答的那样,native关键字用于调用本机代码。

GWT还使用它来实现javascript方法。


13

实现本机代码的函数被声明为本机。

Java本机接口(JNI)是一个编程框架,使在Java虚拟机(JVM)中运行的Java代码能够调用本机应用程序(特定于硬件和操作系统平台的程序)和编写的库,并由它们进行调用。其他语言,例如C,C ++和汇编语言。

http://en.wikipedia.org/wiki/Java_Native_Interface


8

NATIVE是非访问修饰符。它只能应用于METHOD。它指示方法或代码的PLATFORM-DEPENDENT实现。


6

native是java中的关键字,它用于使未实现的结构(方法)像抽象的一样,但是它将依赖于平台(例如本机代码)并从本机堆栈而不是java堆栈执行。


6
  • native 是Java中的关键字,表示平台相关。
  • native方法充当Java(JNI)与其他编程语言之间的接口。

3

native由于功能或性能原因,Java 方法为Java代码提供了一种调用OS本机代码的机制。

例:

606  public native int availableProcessors();
617  public native long freeMemory();
630  public native long totalMemory();
641  public native long maxMemory();
664  public native void gc();

Runtime.classOpenJDK中相应的文件中,位于JAVA_HOME/jmods/java.base.jmod/classes/java/lang/Runtime.class,包含这些方法并用ACC_NATIVE0x0100)标记,并且这些方法不包含Code属性,这意味着这些方法在Runtime.class文件中没有任何实际的编码逻辑:

  • 方法13 availableProcessors:标记为本机且没有Code属性
  • 方法14 freeMemory:标记为本机且没有Code属性
  • 方法15 totalMemory:标记为本机且没有Code属性
  • 方法16 maxMemory:标记为本机且没有Code属性
  • 方法17 gc:标记为本机且没有代码属性

在此处输入图片说明

实际上,编码逻辑位于相应的Runtime.c文件中:

42  #include "java_lang_Runtime.h"
43
44  JNIEXPORT jlong JNICALL
45  Java_java_lang_Runtime_freeMemory(JNIEnv *env, jobject this)
46  {
47      return JVM_FreeMemory();
48  }
49
50  JNIEXPORT jlong JNICALL
51  Java_java_lang_Runtime_totalMemory(JNIEnv *env, jobject this)
52  {
53      return JVM_TotalMemory();
54  }
55
56  JNIEXPORT jlong JNICALL
57  Java_java_lang_Runtime_maxMemory(JNIEnv *env, jobject this)
58  {
59      return JVM_MaxMemory();
60  }
61
62  JNIEXPORT void JNICALL
63  Java_java_lang_Runtime_gc(JNIEnv *env, jobject this)
64  {
65      JVM_GC();
66  }
67  
68  JNIEXPORT jint JNICALL
69  Java_java_lang_Runtime_availableProcessors(JNIEnv *env, jobject this)
70  {
71      return JVM_ActiveProcessorCount();
72  }

并将这些C编码编译到位于以下位置的libjava.so(Linux)或libjava.dll(Windows)文件中JAVA_HOME/jmods/java.base.jmod/lib/libjava.so

在此处输入图片说明

在此处输入图片说明

参考

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.