Java内在函数在各个地方都有提及(例如here)。我的理解是,这些是使用特殊本机代码处理的方法。这似乎类似于JNI方法,它也是本机代码的一部分。
有什么区别?
答案 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编译器对前者的工作了解为零,因此无法跨调用边界应用各种优化。例如内联。