Java内在方法和本机方法之间有什么区别?

rgh*_*ome 6 java native intrinsics

Java固有函数在很多地方都提到过(例如here)。我的理解是,这些是使用特殊本机代码处理的方法。这似乎类似于JNI方法,它也是本机代码块。

有什么不同?

Pav*_*nov 8

主要区别在于,JVM 知道内在方法的实现,并且可以使用依赖于机器的,经过优化的指令(有时甚至使用单个处理器指令)来代替原始的Java代码,而JNI方法的实现是未知的一个JVM。

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

PS您提供的链接包含该特定JVM的已知方法的列表。此列表可能因一个JVM而异。


Pet*_*des 7

JIT知道内在函数,因此可以将相关的机器指令内联到它的JITing代码中,并作为热循环的一部分对其进行优化。

JNI函数对于编译器来说是100%的黑匣子,具有相当大的调用/返回开销(尤其是如果仅将其用于标量的话)。

但是,即使只是int bitcount(unsigned x){ return __builtin_popcount(x); }对编译为x86-64 的函数的调用popcnt eax, ediret(x86-64 System V调用约定)(JIT编译器发出的调用者)仍必须假定所有被调用阻塞的寄存器都被破坏了。在x86-64上,这是大多数整数寄存器和所有FP /向量寄存器。(就像调用黑盒函数与内部函数的提前C ++编译器的成本一样)。但是我怀疑调用JNI函数的成本还包括一些额外的开销。

当然,对任何未知函数的调用都意味着,如果JIT编译器无法证明没有其他东西可以引用它们,那么寄存器中的变量可能需要同步到内存。(转义分析。)

另外,内在函数意味着JVM可以了解函数的功能,并可以通过该函数进行优化。例如,在持续传播的情况下,它知道popcount(5)= 2个设置位。但是对于实际的JNI函数,它仍然必须调用它。除非有某种方法将该函数声明为“ pure”,以便CSE进行,否则每个调用都是明显的副作用。

使用大量内联,编译时间常数并不罕见。


Ste*_*n C 6

“本机”方法是一个广义术语,表示该方法在JVM本身或动态加载的本机库中实现。

native方法是声明为方法native在类的Java源代码。

“固有”方法是JVM运行时(特别是JIT编译器)对其执行特殊优化的方法。“固有的”含义之一就是调用序列不是JNI调用。但是优化可能会更广泛。

请注意,native和“ intrisic”是正交的:

  • 方法既可以是native“本征的”,也可以是“本征的”。例如arraycopy。同时native“本征”和“固有”的方法将不会实现为JNI方法。
  • 一种方法可能是“内在的”而不是native;例如某些StringJava版本中的某些方法。在这种情况下,该方法的JIT编译版本将忽略Java源代码及其字节代码。

这似乎类似于JNI方法,它也是本机代码块。

JNI是用于实现native非“内部”方法的API 。因此,JNI方法是在C / C ++中实现的方法,其签名与JNI调用序列兼容。

问题在于,JNI方法调用序列比典型的Java到Java或Java到内部调用序列更重。(这是由于JNI调用的通用性质,以及需要在Java对应的C / C ++类型之间检查并映射参数/结果……之类的东西。)

与Java和内在方法相比,JNI方法的另一个问题是JIT编译器对前者的工作了解为零,因此无法跨调用边界应用各种优化。例如内联。