小编典典
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."":()V
4: return
public final void baz();
Code:
0: sipush 300
3: istore_1
4: return
}
请注意,字节码有一个sipush 300替代aload的getfieldS和一个iadd。300是计算值。private
final变量也是如此。如果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."":()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: return
public 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."":()V
4: return
private void baz();
Code:
0: return
private java.lang.String foo();
Code:
0: aload_0
1: invokespecial #2; //Method bar:()Ljava/lang/String;
4: areturn
private 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中使用的性能技术。
2020-09-11