javac将提供字节码,该字节码是生成该字节码的原始Java程序的忠实表示(在某些可以优化的特定情况下除外: 常量折叠 和 消除死代码
)。但是,当JVM使用JIT编译器时,可以执行优化。
对于第一种情况,JVM似乎支持内联(请参见此处的“
方法”
下的内容,以及有关JVM的内联示例的信息,请参见此处)。
我找不到自己执行方法内联的任何示例
javac。我尝试编译一些示例程序(类似于您在问题中描述的程序),即使是,它们似乎都没有直接内联该方法
final。这些优化似乎是由JVM的JIT编译器而非进行的
javac。下提到的“编译”
的方法
在这里似乎是热点JVM的JIT编译器,而不是
javac。
据我所知,它
javac支持 死代码消除 (请参阅第二种情况的示例)和 常量折叠
。在常量折叠中,编译器将预先计算常量表达式并使用计算出的值,而不是在运行时执行计算。例如:
public class ConstantFolding { private static final int a = 100; private static final int b = 200; public final void baz() { int c = a + b; }}编译为以下字节码:
Compiled from "ConstantFolding.java"public class ConstantFolding extends java.lang.Object{private static final int a;private static final int b;public ConstantFolding(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: returnpublic final void baz(); Code: 0: sipush 300 3: istore_1 4: return}请注意,字节码有一个
sipush 300替代
aload的
getfieldS和一个
iadd。
300是计算值。
privatefinal变量也是如此。如果
a和
b不是静态的,则产生的字节码将是:
Compiled from "ConstantFolding.java"public class ConstantFolding extends java.lang.Object{private final int a;private final int b;public ConstantFolding(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: aload_0 5: bipush 100 7: putfield #2; //Field a:I 10: aload_0 11: sipush 200 14: putfield #3; //Field b:I 17: returnpublic final void baz(); Code: 0: sipush 300 3: istore_1 4: return}这里也
sipush 300使用了。
对于第二种情况(消除死代码),我使用了以下测试程序:
public class InlineTest { private static final boolean debug = false; private void baz() { if(debug) { String a = foo(); } } private String foo() { return bar(); } private String bar() { return "abc"; }}给出以下字节码:
Compiled from "InlineTest.java"public class InlineTest extends java.lang.Object{private static final boolean debug;public InlineTest(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: returnprivate void baz(); Code: 0: returnprivate java.lang.String foo(); Code: 0: aload_0 1: invokespecial #2; //Method bar:()Ljava/lang/String; 4: areturnprivate java.lang.String bar(); Code: 0: ldc #3; //String abc 2: areturn}如您所见,
foo根本不会调用,
baz因为该
if块内的代码实际上是“死”的。
Sun(现在是Oracle)的HotSpot
JVM结合了字节码的解释和JIT编译。当将字节码提供给JVM时,代码将首先进行解释,但是JVM将监视字节码并挑选出经常执行的部分。它将这些部分覆盖到本机代码中,以便它们可以更快地运行。对于不经常使用的字节码,此编译不会完成。同样也因为编译有一些开销。因此,这实际上是一个权衡的问题。如果决定将所有字节码编译为本机代码,则该代码可能会具有很长的启动延迟。
除了监视字节码之外,JVM还可在解释和加载字节码时对其执行静态分析,以执行进一步的优化。
如果您想了解JVM执行的特定类型的优化,那么Oracle的此页面将非常有帮助。它描述了HotSpot
JVM中使用的性能技术。



