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

时间:2019-06-21 07:28:09

标签: java native intrinsics

Java内在函数在各个地方都有提及(例如here)。我的理解是,这些是使用特殊本机代码处理的方法。这似乎类似于JNI方法,它也是本机代码的一部分。

有什么区别?

3 个答案:

答案 0 :(得分:7)

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

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

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

答案 1 :(得分:6)

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

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

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

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

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

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

答案 2 :(得分:4)

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

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

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

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

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

  

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

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

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

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