gpt4 book ai didi

java - 优化期间会使用 Java 内联方法吗?

转载 作者:IT老高 更新时间:2023-10-28 21:15:20 25 4
gpt4 key购买 nike

不知道JVM/javac是不是聪明到可以转

// This line...
string a = foo();

string foo()
{
return bar();
}

string bar()
{
return some-complicated-string computation;
}

进入

string a = bar();

或者在发布情况下去掉对 foo() 的不必要调用(因为无法访问代码):

string a = foo(bar());

// bar is the same
...

string foo(string b)
{
if (debug) do-something-with(b);
}

第一个例子我的感觉是肯定的,第二个例子“不太确定”,但是谁能给我一些指示/链接来确认?

最佳答案

javac 将呈现字节码,该字节码是生成字节码的原始 Java 程序的忠实表示(除了在某些可以优化的情况下:常量折叠死代码消除)。但是,JVM 在使用 JIT 编译器时可能会执行优化。

对于第一种情况,JVM 似乎支持内联(参见 方法 herehere 下的 JVM 内联示例)。

我找不到任何由 javac 本身执行的方法内联示例。我尝试编译一些示例程序(类似于您在问题中描述的那个),但即使是 final,它们似乎都没有直接内联该方法。这些优化似乎是由 JVM 的 JIT 编译器完成的,而不是由 javac 完成的。 方法中提到的“编译器”here似乎是 HotSpot 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: return

public final void baz();
Code:
0: sipush 300
3: istore_1
4: return

}

注意字节码有一个 sipush 300 而不是 aloadgetfields 和一个 iadd300 是计算出来的值。 private final 变量也是如此。如果 ab 不是静态的,则生成的字节码将是:

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: 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."<init>":()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

}

如您所见,在 baz 中根本没有调用 foo,因为 if block 内的代码实际上是“死的” .

Sun(现在是 Oracle)的 HotSpot JVM 结合了字节码解释和 JIT 编译。当字节码呈现给 JVM 时,代码最初会被解释,但 JVM 会监视字节码并挑选出经常执行的部分。它将这些部分转换为 native 代码,以便它们运行得更快。对于不经常使用的一段字节码,不进行此编译。这也很好,因为编译有一些开销。所以这真的是一个权衡的问题。如果你决定将所有字节码都编译为nativecode,那么代码可能会有很长的启动延迟。

除了监控字节码,JVM 还可以在解释和加载字节码时对字节码进行静态分析,以进行进一步的优化。

如果您想了解 JVM 执行的具体优化类型,this page在甲骨文非常有帮助。它描述了 HotSpot JVM 中使用的性能技术。

关于java - 优化期间会使用 Java 内联方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7772864/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com