优化期间的 Java 内联方法吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7772864/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 21:22:03  来源:igfitidea点击:

Would Java inline method(s) during optimization?

javaoptimizationjvmjavac

提问by Schultz9999

I wonder if JVM/javac is smart enough to turn

我想知道 JVM/javac 是否足够聪明,可以转

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

string foo()
{
  return bar();
}

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

into

进入

string a = bar();

Or strip unnecessary call to foo() in release case (because unreachable code):

或者在发布案例中删除对 foo() 的不必要调用(因为无法访问代码):

string a = foo(bar());

// bar is the same
...

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

My feeling is yes for the first example and "not so sure" for the second one, but could anyone give me some pointers/links to confirm that?

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

回答by Vivin Paliath

javacwill present bytecode that is a faithful representation of the original Java program that generated the bytecode (except in certain situations when it can optimize: constant foldingand dead-code elimination). However, optimization may be performed by the JVM when it uses the JIT compiler.

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

For the first scenario it looks like the JVM supports inlining (see under Methodshereand see herefor an inlining example on the JVM).

对于第一种情况,JVM 似乎支持内联(请参阅此处的方法部分并参阅此处的有关 JVM 的内联示例)。

I couldn't find any examples of method inlining being performed by javacitself. I tried compiling a few sample programs (similar to the one you have described in your question) and none of them seemed to directly inline the method even when it was final. It would seem that these kind of optimizations are done by the JVM's JIT compiler and not by javac. The "compiler" mentioned under Methodshereseems to be the HotSpot JVM's JIT compiler and not javac.

我找不到任何单独执行方法内联的示例javac。我尝试编译一些示例程序(类似于您在问题中描述的程序),但它们似乎都没有直接内联该方法,即使它是final. 这些优化似乎是由 JVM 的 JIT 编译器完成的,而不是由javac. 此处方法下提到的“编译器”似乎是 HotSpot JVM 的 JIT 编译器,而不是.javac

From what I can see, javacsupports dead-code elimination(see the example for the second case) and constant folding. In constant folding, the compiler will precalculate constant expressions and use the calculated value instead of performing the calculation during runtime. For example:

据我所知,javac支持死代码消除(参见第二种情况的示例)和常量折叠。在常量折叠中,编译器将预先计算常量表达式并使用计算出的值,而不是在运行时执行计算。例如:

public class ConstantFolding {

   private static final int a = 100;
   private static final int b = 200;

   public final void baz() {
      int c = a + b;
   }
}

compiles to the following bytecode:

编译为以下字节码:

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

}

Note that the bytecode has an sipush 300instead of aload's getfields and an iadd. 300is the calculated value. This is also the case for private finalvariables. If aand bwere not static, the resulting bytecode will be:

请注意,字节码有一个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

}

Here also, an sipush 300is used.

这里也sipush 300使用了 an 。

For the second case (dead-code elimination), I used the following test program:

对于第二种情况(死代码消除),我使用了以下测试程序:

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";
   }
}

which gives the following bytecode:

它给出了以下字节码:

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

}

As you can see, the foois not called at all in bazbecause the code inside the ifblock is effectively "dead".

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

Sun's (now Oracle's) HotSpot JVM combines interpretation of the bytecode as well as JIT compilation. When bytecode is presented to the JVM the code is initially interpreted, but the JVM will monitor the bytecode and pick out parts that are frequently executed. It coverts these parts into native code so that they will run faster. For piece of bytecode that are not used so frequently, this compilation is not done. This is just as well because compilation has some overhead. So it's really a question of tradeoff. If you decide to compile all bytecode to nativecode, then the code can have a very long start-up delay.

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

In addition to monitoring the bytecode, the JVM can also perform static analysis of the bytecode as it is interpreting and loading it to perform further optimization.

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

If you want to know the specific kinds of optimizations that the JVM performs, this pageat Oracle is pretty helpful. It describes the performance techniques used in the HotSpot JVM.

如果您想了解 JVM 执行的特定类型的优化,Oracle 上的这个页面非常有帮助。它描述了 HotSpot JVM 中使用的性能技术。

回答by Steve Kuo

The JVM will most likely inline. In general it's best to optimize for human readability. Let the JVM do the runtime optimization.

JVM 很可能会内联。一般来说,最好针对人类可读性进行优化。让JVM做运行时优化。

JVM expert Brian Goetz saysfinalhas no impact on methods being inlined.

JVM 专家Brian Goetz 表示final对内联方法没有影响。

回答by ratchet freak

in the same class file the javac will be able to inline staticand final(other class files might change the inlined function)

在同一个类文件中,javac 将能够内联staticfinal(其他类文件可能会更改内联函数)

however the JIT will be able to optimize much more (including inlining superfluous removing bounds- and null checks, etc.) because it knows more about the code

但是 JIT 将能够优化更多(包括内联多余的删除边界和空检查等),因为它对代码了解更多

回答by Hot Licks

A "highly optimizing" JIT compiler will inline both cases (and, @Mysticial, it might even inline some polymorphic cases, by employing various forms of trickery).

“高度优化”的 JIT 编译器将内联这两种情况(并且,@Mysticial,它甚至可能通过采用各种形式的技巧来内联一些多态情况)。

You can increase the chances of inlining by making methods final, and a few other tricks.

您可以通过使方法最终化和其他一些技巧来增加内联的机会。

javac does some primitive inlining, mostly of final/private methods, primarily intended to help out some conditional compilation paradigms.

javac 做了一些原始的内联,主要是最终/私有方法,主要是为了帮助一些条件编译范例。

回答by yoprogramo

If you thrown an exception in bar() and print the stacktrace you'll see the whole path of calls... I think java honor all of them.

如果您在 bar() 中抛出异常并打印堆栈跟踪,您将看到调用的整个路径......我认为 java 尊重所有这些。

The second case is the same, debug is just a variable of your system, not a define as in C++, so it is mandatory to evaluate it before.

第二种情况相同,debug 只是您系统的一个变量,而不是 C++ 中的定义,因此必须先对其进行评估。

回答by dma_k

I might be wrong, but my feeling is "no in all cases". Because your string bar()can be overridden by overloaded by other classes in the same package. finalmethods are good candidates, but it depends on JIT.

我可能是错的,但我的感觉是“在所有情况下都不是”。因为您string bar()可以被同一包中的其他类重载覆盖。final方法是很好的候选者,但这取决于 JIT。

Another interesting note is here.

另一个有趣的注意事项在这里