Java 内部方法和本机方法有什么区别?

What is the difference between Java intrinsic and native methods?

Java 内部函数在不同的地方被提及(例如 here)。我的理解是这些是用特殊本机代码处理的方法。这看起来类似于也是本机代码块的 JNI 方法。

有什么区别?

主要区别在于 JVM 知道 内部方法的实现,并且可以用 machine-dependent well-optimized 替换原来的 java-code指令(有时甚至使用单个处理器指令),而 JNI 方法的实现对于 JVM 未知

后者施加了一些限制,例如不可能对 JNI 方法应用某些优化技术,需要在调用堆栈上做额外的工作等。

P.S。您提供的 link 包含该特定 JVM 的已知方法列表。此列表可能因 JVM 而异。

JIT 了解内部函数,因此它可以将相关的机器指令内联到它正在 JIT 的代码中,并作为热循环的一部分围绕它进行优化。

JNI 函数对于编译器来说是一个 100% 的黑盒,具有显着的 call/return 开销(特别是如果您仅将其用于标量)。

但即使它只是对编译为 x86-64 popcnt eax, ediint bitcount(unsigned x){ return __builtin_popcount(x); } 之类的函数的调用; ret(x86-64 System V 调用约定)调用者(JIT 编译器正在发出)仍然必须假设所有 call-clobbered 寄存器都被破坏了。在 x86-64 上,这是大部分整数寄存器和所有 FP/vector 寄存器。 (就像 ahead-of-time C++ 编译器调用 black-box 函数与内部函数的成本一样)。但我怀疑调用 JNI 函数的成本包括一些额外的开销。

当然,对任何未知函数的调用意味着如果 JIT 编译器无法证明没有其他对象引用它们,则可能需要将寄存器中的变量同步到内存中。 (逃逸分析。)

此外,内在函数意味着 JVM 理解函数的作用,并可以通过它进行优化。例如通过不断传播,它知道 popcount(5) = 2 设置位。但是对于实际的 JNI 函数,它仍然必须调用它。并且每个调用都是可见的 side-effect 除非有某种方法可以将函数声明为 "pure" 所以它可以 CSE.

大量内联时,编译时常量并不少见。

"native" 方法是一个广义术语,意思是该方法在 JVM 本身或动态加载的本机库中实现。

native 方法是在 class.

的 Java 源代码中声明为 native 的方法

"intrinsic" 方法是 JVM 运行时(特别是 JIT 编译器)对其执行特殊优化的方法。 "intrinsic" 的意思之一是调用序列不是 JNI 调用。但优化可能比这更广泛。

注意 native 和 "intrisic" 是正交的:

  • 一个方法可以同时是native和"intrinsic";例如 arraycopy。既是 native 又是 "intrinsic" 的方法(通常)不会作为 JNI 方法实现。
  • 一个方法可能是"intrinsic"而不是native;例如 Java 的某些版本中的某些 String 方法。在这种情况下,Java 源代码及其字节码在该方法的 JIT 编译版本中被忽略。

This seems similar to a JNI method which is also a block of native code.

JNI 是一个 API,用于实现 native 不是 "intrinsic" 的方法。所以 JNI 方法是用 C/C++ 实现的方法,其签名与 JNI 调用序列兼容。

问题是 JNI 方法调用序列比典型的 Java-to-Java 或 Java-to-intrinsic 调用序列更重。 (这是由于 JNI 调用的通用性质,以及需要在 Java 对应的 C/C++ 类型之间检查和映射参数/结果......等等。)

与 Java 和内部方法相比,JNI 方法的另一个问题是 JIT 编译器对前者的行为知之甚少,因此无法跨调用边界应用各种优化;例如内联。