Java: How will JVM optimise the call to a void and empty function?

前端 未结 6 905
囚心锁ツ
囚心锁ツ 2020-12-15 23:35

Let\'s imagine we have the following classes:

public class Message extends Object {}

public class Logger implements ILogger {
 public void log(Message m) {/         


        
相关标签:
6条回答
  • 2020-12-16 00:02

    I don't think java compiler will remove the call because the called method is empty, because you can change the method in future also without making any change in the main method.

    0 讨论(0)
  • 2020-12-16 00:03

    Disassembling the following file (with javap -c) suggests they are not stripped out by the 1.7.0 compiler when compiling down to bytecode:

    public class Program
    {
        public static class Message extends Object {}
    
        public interface ILogger {
            void log(Message m);
        }
    
        public static class Logger implements ILogger {
            public void log(Message m) { /* empty */ }
        }
    
        public static void main(String[] args) {
            ILogger l = new Logger();
            l.log((Message)null); // a)
            l.log(new Message()); // b)
        }
    }
    

    The result is below. The key bits are the invokes on lines 13 and 26.

    Compiled from "Program.java"
    public class Program {
      public Program();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public static void main(java.lang.String[]);
        Code:
           0: new           #2                  // class Program$Logger
           3: dup
           4: invokespecial #3                  // Method Program$Logger."<init>":()V
           7: astore_1
           8: aload_1
           9: aconst_null
          10: checkcast     #4                  // class Program$Message
          13: invokeinterface #5,  2            // InterfaceMethod Program$ILogger.log:(LProgram$Message;)V
          18: aload_1
          19: new           #4                  // class Program$Message
          22: dup
          23: invokespecial #6                  // Method Program$Message."<init>":()V
          26: invokeinterface #5,  2            // InterfaceMethod Program$ILogger.log:(LProgram$Message;)V
          31: return
    }
    

    EDIT: However, as @mikera pointed out, it's likely that the JIT compiler will do further optimizations when the program runs, which may be able to eliminate the calls. I don't know enough about the details to comment on that unfortunately.

    SIDE NOTE: You might be interested in this link, which deals with performance techniques used by the Hotspot JVM:

    https://wikis.oracle.com/display/HotSpotInternals/PerformanceTechniques

    0 讨论(0)
  • 2020-12-16 00:06

    It is impossible to say definitively - it will depend on the implementation of the JVM / Java compiler.

    A smart enough compiler can prove that neither statements have an effect, and can therefore eliminate them. I believe most modern JVMs will do this, though you would need to test on your specific configuration to be sure.

    a) is easier to optimise away than b) since b) includes a constructor call, which the compiler also needs to prove has no side effects before it can optimise away the whole statement.

    Note that you'd expect this kind of elimination to be done by the JIT compiler rather than the Java compiler itself, i.e. bytecode would probably be generated that included the log function calls, but this is later optimised away by the JIT compiler when it compiles down to native code.

    Also, since the JIT can recompile on runtime statistics etc., it is possible that the code will be there to start with, but get compiled away later on successive optimisations.

    0 讨论(0)
  • 2020-12-16 00:12

    Will the Java compiler strip out statements a and b ?

    The javac (source to bytecode) compiler won't strip either call. (It is easy to check this by examining the bytecodes; e.g. looking at the javap -c output.)

    In both cases (stripping or not stripping), what is the rationale behind the Java compiler's decision ?

    Conformance with the JLS :-).

    From a pragmatic perspective:

    • If the javac compiler optimized the calls away, a Java debugger wouldn't be able to see them at all ... which would be rather confusing for the developer.
    • Early optimization (by javac) would result in breakage if the Message class and the main class were compiled / modified independently. For example, consider this sequence:

      • Message is compiled,
      • the main class is compiled,
      • Message is edited so that log does something ... and recompiled.

      Now we have an incorrectly compiled main class that doesn't do the right thing at a and b because the prematurely inlined code is out of date.


    However, the JIT compiler might optimize the code at runtime in a variety of ways. For instance:

    • The method calls in a and b may be inlined if the JIT compiler can deduce that no virtual method dispatching is required. (If Logger is the only class used by the application that implements ILogger this a no-brainer for a good JIT compiler.)

    • After inlining the first method call, the JIT compiler may determine that the body is a noop and optimize the call away.

    • In the case of the second method call, the JIT compiler could further deduce (by escape analysis) that the Message object doesn't need to be allocated on the heap ... or indeed at all.

    (If you want to know what the JIT compiler (on your platform) actually does, Hotspot JVMs have a JVM option that dumps out the JIT-compiled native code for selected methods.)

    0 讨论(0)
  • 2020-12-16 00:17

    Probably eventually, definitely not immediately, and not necessarily ever. The JIT makes no guarantees, especially not for a method that only gets called a couple times. (It would probably get categorized as simply inlining the log call, and the inlined code happening to be...nothing.)

    0 讨论(0)
  • 2020-12-16 00:21

    The Server JIT will certainly inline and eventually eliminate the code altogether if you make the reference final: final ILogger l = new Logger(); In a modern JVM most of the optimization is performed by the JIT.

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