Would Java inline method(s) during optimization?

后端 未结 6 1923
青春惊慌失措
青春惊慌失措 2020-11-29 05:46

I wonder if JVM/javac is smart enough to turn

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

string foo()
{
  return bar();
}

string bar()
{
  return some-complicated-s         


        
相关标签:
6条回答
  • 2020-11-29 06:18

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

    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.

    0 讨论(0)
  • 2020-11-29 06:20

    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.

    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.

    0 讨论(0)
  • 2020-11-29 06:25

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

    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

    0 讨论(0)
  • 2020-11-29 06:34

    javac will 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 folding and dead-code elimination). However, optimization may be performed by the JVM when it uses the JIT compiler.

    For the first scenario it looks like the JVM supports inlining (see under Methods here and see here for an inlining example on the JVM).

    I couldn't find any examples of method inlining being performed by javac itself. 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 Methods here seems to be the HotSpot JVM's JIT compiler and not javac.

    From what I can see, javac supports 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:

    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 300 instead of aload's getfields and an iadd. 300 is the calculated value. This is also the case for private final variables. If a and b were not static, the resulting bytecode will be:

    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 300 is used.

    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 foo is not called at all in baz because the code inside the if block is effectively "dead".

    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.

    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.

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

    0 讨论(0)
  • 2020-11-29 06:37

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

    JVM expert Brian Goetz says final has no impact on methods being inlined.

    0 讨论(0)
  • 2020-11-29 06:41

    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. final methods are good candidates, but it depends on JIT.

    Another interesting note is here.

    0 讨论(0)
提交回复
热议问题