Java:JVM将如何优化对void函数的调用?
问题内容:
假设我们有以下类:
public class Message extends Object {}
public class Logger implements ILogger {
public void log(Message m) {/*empty*/}
}
和以下程序:
public static void main(String args[]) {
ILogger l = new Logger();
l.log((Message)null); // a)
l.log(new Message()); // b)
}
Java编译器是否会去除语句 a 和 b ?在这两种情况下(剥离还是不剥离),Java编译器决定背后的原理是什么?
问题答案:
Java编译器将去掉语句
a
和b
?
的javac
(源到字节码)编译器不会破坏任何呼叫。(通过检查字节码很容易检查这一点;例如,查看javap -c
输出。)
在这两种情况下(剥离还是不剥离),Java编译器决定背后的原理是什么?
符合JLS :-)。
从务实的角度:
- 如果
javac
编译器优化了这些调用,则Java调试器将根本看不到它们,这对于开发人员来说会造成混乱。 -
javac
如果Message
类和主类是独立编译/修改的,则早期优化(by )将导致破坏。例如,考虑以下顺序:Message
被编译,- 主类被编译,
Message
进行了编辑,以便log
执行某些操作并重新编译。
现在我们有一个不正确编译主类,没有做的事情,在a
和b
由于过早内联代码是过时。
但是,JIT编译器 可能 以多种方式 在运行时 优化代码。例如:
-
该方法要求在
a
和b
可被内联如果JIT编译器可以推断出没有虚拟方法调度是必需的。(IfLogger
是应用程序使用的唯一实现该类的类,ILogger
对于优质的JIT编译器而言,这是不言而喻的。) -
内联第一个方法调用后,JIT编译器可以确定主体为noop并优化调用。
-
在第二个方法调用的情况下,JIT编译器可以进一步(通过转义分析)推断出
Message
不需要在堆上分配对象,或者根本不需要分配对象。
(如果您想知道(您的平台上的)JIT编译器的 实际 作用,Hotspot JVM具有一个JVM选项,该选项会为选定的方法转储JIT编译的本机代码。)