我写了一个小的静态JNI函数,它只有5个指令长。 JVM是否可以将此代码内联到一个经常调用它的方法体中,或者它是否总是在JITed方法中生成call
指令?
例如:
public class SomeClass {
private static native long func();
public void doLoop() {
for(int i = 0; i < 0xFFFFFF; i++) {
func();
}
}
public static void main(String[] args) {
for(int i = 0; i < 0xFFFFFF; i++) {
doLoop();
}
}
}
JVM是否可以将func
的代码内嵌到doLoop
中,或者只是将其编译为call func
答案 0 :(得分:11)
不,JVM基本上不能。
本机函数的实现是二进制黑盒子; JVM唯一知道的就是入口点地址。
本机代码不由虚拟机管理,不能在Java方法的上下文中执行。 JVM将&#39; in_java&#39; 状态的线程与&#39; in_native&#39; 中的线程区分开来。例如,本机线程不会在JVM安全点停止,只是因为JVM无法执行此操作。
此外,本机方法调用不是那么简单的操作。需要A special procedure来解决JNI呼叫的所有方面。
答案 1 :(得分:4)
这将是非常困难的并且有大量的角落情况让JVM完全安全地完成它,所以几乎肯定没有。
e.g。考虑以
结尾的C函数return memcpy(dest, src, count);
一个不错的编译器会进行尾调用优化,因此该函数将编译为类似
的函数mov rdi, dest
mov rsi, src
mov edx, count
jmp memcpy
而不是
...
call memcpy
ret
因此,在确定是否/如何内联一段本地机器代码时,JVM必须做的不仅仅是查找ret
指令。
要正确内联本机方法,JVM作者必须考虑每个可能的极端情况。他们很可能不会尝试。
除了棘手的跳转之外,本机代码中的段错误将显示在内联目标中,而不是显示在单独的堆栈帧中。
最佳建议:制作一个包含所需循环的本机函数的版本,或者它是否真的有5个简单的行(不是库函数,或者扩展到很多asm的其他东西),只需用Java编写即可。让JIT编译器担心它。
答案 2 :(得分:0)
从字面上看,本机方法无法内联。
但是,JVM可以用内在函数替换Java方法和本机方法。例如许多不安全的方法被视为内在因素,并且不会为JNI支付预付款。
因此,可以有效地内联本机化的本地方法。