我们假设我们有以下类:
public class Message extends Object {}
public class Logger implements ILogger {
public void log(Message m) {/*empty*/}
}
Run Code Online (Sandbox Code Playgroud)
以及以下程序:
public static void main(String args[]) {
ILogger l = new Logger();
l.log((Message)null); // a)
l.log(new Message()); // b)
}
Run Code Online (Sandbox Code Playgroud)
Java编译器是否会删除语句a和b?在这两种情况下(剥离或不剥离),Java编译器的决定背后的基本原理是什么?
Ste*_*n C 17
Java编译器将去掉语句
a和b?
的javac(源到字节码)编译器不会破坏任何呼叫.(通过检查字节码很容易检查;例如查看javap -c输出.)
在这两种情况下(剥离或不剥离),Java编译器的决定背后的基本原理是什么?
符合JLS :-).
从务实的角度来看:
javac编译器优化了调用,Java调试器将根本无法看到它们......这对开发人员来说会相当混乱.javac如果Message类和主类是独立编译/修改的,那么早期优化(by )会导致破坏.例如,请考虑以下顺序:
Message 编译,Message编辑,以便log做某事......并重新编译.现在我们有一个不正确编译主类,没有做的事情,在a和b由于过早联的代码是过时的.
但是,JIT编译器可能会以各种方式在运行时优化代码.例如:
如果JIT编译器可以推断出不需要虚拟方法调度,则该方法可以调用a并且b可以内联.(如果Logger是应用程序使用的唯一一个类ILogger,它可以很好地为JIT编译器实现这一点.)
在内联第一个方法调用之后,JIT编译器可以确定主体是noop并优化呼叫.
在第二个方法调用的情况下,JIT编译器可以进一步推断(通过转义分析)该Message对象不需要在堆上分配......或者实际上根本不需要.
(如果您想知道JIT编译器(在您的平台上)实际上做了什么,Hotspot JVM有一个JVM选项,可以为所选方法转储JIT编译的本机代码.)
反汇编以下文件(with javap -c)表明在编译为字节码时1.7.0编译器不会删除它们:
public class Program
{
public static class Message extends Object {}
public interface ILogger {
void log(Message m);
}
public static 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)
}
}
Run Code Online (Sandbox Code Playgroud)
结果如下.关键位是第13和26行的调用.
Compiled from "Program.java"
public class Program {
public Program();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class Program$Logger
3: dup
4: invokespecial #3 // Method Program$Logger."<init>":()V
7: astore_1
8: aload_1
9: aconst_null
10: checkcast #4 // class Program$Message
13: invokeinterface #5, 2 // InterfaceMethod Program$ILogger.log:(LProgram$Message;)V
18: aload_1
19: new #4 // class Program$Message
22: dup
23: invokespecial #6 // Method Program$Message."<init>":()V
26: invokeinterface #5, 2 // InterfaceMethod Program$ILogger.log:(LProgram$Message;)V
31: return
}
Run Code Online (Sandbox Code Playgroud)
编辑:但是,正如@mikera指出的那样,JIT编译器可能会在程序运行时进行进一步的优化,这可能会消除调用.遗憾的是,我对细节的评论不够充分.
侧面注意:您可能对此链接感兴趣,该链接涉及Hotspot JVM使用的性能技术:
https://wikis.oracle.com/display/HotSpotInternals/PerformanceTechniques
| 归档时间: |
|
| 查看次数: |
2662 次 |
| 最近记录: |