What is the difference between Java intrinsic and native methods?
The main difference is that a JVM knows the implementation of an intrinsic method and can substitute the original java-code with machine-dependent well-optimized instructions (sometimes even with a single processor instruction), whereas the implementation of a JNI method is unknown to a JVM.
The latter impose some restrictions such as impossibility to apply certain optimization techniques for JNI methods, the need to do an extra work on the call stack, etc.
P.S. The link you have provided contains a list of known methods to that specific JVM. This list may differ from one JVM to another.
The JIT knows about intrinsics, so it can inline the relevant machine instruction into the code it's JITing, and optimize around it as part of a hot loop.
A JNI function is a 100% black box for the compiler, with significant call/return overhead (especially if you use it for just a scalar).
But even if it were just a call to a function like int bitcount(unsigned x){ return __builtin_popcount(x); }
that compiled to x86-64 popcnt eax, edi
; ret
(x86-64 System V calling convention) the caller (which the JIT compiler is emitting) would still have to assume that all the call-clobbered registers were clobbered. On x86-64, that's most of the integer registers and all the FP/vector registers. (Just like the cost for an ahead-of-time C++ compiler for calling a black-box function vs. an intrinsic). But I suspect the cost for calling a JNI function includes some extra overhead on top of that.
And of course a call to any unknown function means that variables which were in registers might need to be synced to memory if the JIT compiler can't prove that nothing else has a reference to them. (Escape analysis.)
Plus, intrinsics mean the JVM understands what the function does, and can optimize through it. e.g. with constant propagation, it knows that popcount(5) = 2 set bits. But with an actual JNI function, it would still have to call it. And every call is a visible side-effect unless there's some way to declare the function as "pure" so it can CSE.
With heavy inlining, compile time constants are not rare.
A "native" method is a broad term meaning that the method is implemented either in the JVM itself or a dynamically loaded native library.
A native
method is a method that is declared as native
in the Java source code of the class.
An "intrinsic" method is one that the JVM runtime (specifically the JIT compiler) performs special optimization on. One of the things that "intrinsic" means is that the call sequence is not a JNI call. But the optimizations may be more extensive than that.
Note that native
and "intrisic" are orthogonal:
- A method may be both
native
and "intrinsic"; for examplearraycopy
. A method that is bothnative
and "intrinsic" will (typically) not be implemented as a JNI method. - A method may be "intrinsic" without being
native
; for example someString
methods in some versions of Java. In this case, the Java source code and its bytcodes are ignored in the JIT compiled version of the method.
This seems similar to a JNI method which is also a block of native code.
JNI is an API for implementing native
methods that are not "intrinsic". So a JNI method is a method implemented in C / C++ with a signature that is compatible with a JNI calling sequence.
The problem is that the JNI method call sequence is more heavy weight than a typical Java-to-Java or Java-to-intrinsic calling sequence. (This is due to the generic nature of a JNI call, and the need to check and map arguments / results between Java corresponding C/C++ types ... and things like that.)
The other problem with JNI methods compared with Java and intrinsic methods is that the JIT compiler has zero knowledge of what JNI methods are actually doing, and is therefore unable to apply various optimizations across the call boundary; e.g. inlining.