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

标签 java optimization jvm javac

不知道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/

相关文章:

python - 正则表达式性能Python

MySQL - 我可以避免这些相关/依赖子查询吗?

c++ - 为什么库需要硬编码矢量化而不是编译器自动矢量化

java - JVM 有问题的框架 - EXCEPTION_ACCESS_VIOLATION 错误

Java堆外内存和大页面

java - jpql 根据另一个表中的某些属性从表中选择对象?

java - 我无法让我的嵌套 for 循环参与

java - Swing问题/JTree/自定义树模型

java - 有没有办法在 java 语言(非 native )JVM 代理中启用 "native method prefix"功能?

java - Spring Cloud Stream 应用程序启动器在 10 秒后失败,显示 Bindexception : Cannot initialize binder