为什么 Java 编译器不将这些调用内联到单个 if 子句中?

All*_*lan 1 java performance jvm bytecode javac

我被拉入了类似于以下代码的性能调查:

private void someMethod(String id) {
   boolean isHidden = someList.contains(id);
   boolean isDisabled = this.checkIfDisabled(id);

   if (isHidden && isDisabled) {
     // Do something here
   }
}
Run Code Online (Sandbox Code Playgroud)

当我开始调查它时,我希望编译后的版本看起来像这样:

private void someMethod(String id) {
   if (someList.contains(id) && this.checkIfDisabled(id)) {
     // Do something here
   }
}
Run Code Online (Sandbox Code Playgroud)

然而,令我惊讶的是,编译后的版本看起来和第一个版本一模一样,带有局部变量,这导致 in 中的方法isDisabled总是被调用,这就是性能问题所在。

我的解决方案是自己内联它,所以该方法现在在 处短路isHidden,但它让我想知道:为什么 Java 编译器在这种情况下不够聪明,无法为我内联这些调用?它真的需要局部变量吗?

谢谢 :)

Joa*_*uer 8

第一:java编译器(javac)几乎不做任何优化,该工作几乎完全由JVM本身在运行时完成。

第二:只有当优化代码与未优化代码的行为没有明显差异时才能进行此类优化。

因为我们不知道(编译器大概也不知道)是否checkIfDisabled有任何可观察到的副作用,因此必须假设它可能存在。因此,即使知道不需要该方法的返回值,也无法优化对该方法的调用。

然而,有一个选项可以在运行时完成这种优化:如果方法的主体(或多个主体,由于多态性)足够简单,那么运行时checkIfDisabled很可能可以 优化该代码,如果它认识到这些调用永远不会产生副作用(但我不知道是否有任何 JVM 实际上执行了这种特定类型的优化)。

但这种优化只有在有明确的信息时才可能实现checkIfDisabled。由于 Java 的动态类加载特性,这基本上意味着它几乎永远不会在编译时加载。

一般来说,虽然一些小的优化可能在编译时完成,但在运行时可能的优化范围要大得多(由于可用代码的信息量大大增加),因此 Java 设计者决定基本上将所有优化放在一起努力投入到系统的运行时部分。

  • 补充一点:即使运行时的代码恰好与编译时版本相同,调试器等工具的潜在存在仍然要求编译器不会消除正式规范所需的方法调用。只有知道没有副作用、没有断点等的运行时优化器才能消除这样的方法调用。 (2认同)