Skip to main content

Method inlining in Java JVM

What method inlining is?

Inlining is optimisation in that it replaces the calling method with the body of the called method. When the calling method, the JVM should allocate the stack frame on the stack after the method call returns to the calling method. Inlining improves performance by avoiding the overhead of allocating the stack frame. Docs Version Dropdown

Which flags control inlining?

-XX:+PrintFlagsFinal - shows active flags when the application starts.

We will be interested in some of these:

  • CompileThreshold - number of method calls before the method is considered "hot".
  • MaxInlineLevel - defines a method called chain constraint. The default value is 9.
  • MaxInlineSize - defines the maximum size of a method in bytecode for inlining. Default value 35. If the method is not "hot" and its bytecode size is greater than 35 bytes then it will not be inlined.
  • FreqInlineSize - defines the maximum size of a hot method. Default depends on platform, on my laptop 325 bytes.

How does JIT consider a method for inlining?

The JIT compiler tries to perform inlining based on two things:

  • How many times a method has been called.
  • The size of a method.

The main decision about a method should be inlined, depending on its size and how "hot" it is. The JVM considers the method to be hot based on its internal logic. There aren't any configurable options that directly control this process. If the method is considered for inlining because it has been called a lot, it will only be inlined if its bytecode size is less than 325 bytes. On the other hand, it was considered for inlining if its bytecode size was less than 35 bytes.

The JIT generally inlines static, private or final methods. And while public methods are also candidates for inlining, not every public method will necessarily be inlined. The JVM must determine that there's only one implementation of such a method.

Example of inlining

Consider listing below.

public class App {
public static void main(String[] args) {
long upto = Long.parseLong(args[0]);

for(int i = 0; i < upto; i++) {
int x = inline1();
}
}

public static int inline1() {
return inline2();
}

public static int inline2() {
int x = 3;
int y = inline3() + x;
return y;
}

public static int inline3() {
return inline4();
}

public static int inline4() {
return 3;
}
}
mvn verify
java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -cp target/inlining-1.0-SNAPSHOT.jar com.rkdeep.App 10000

Compilation message

  1. "Called is too big" - the message is printed by the C1 compiler if the size of the called method exceeds the VM parameter -XX:MaxInlineSize=35.
  2. "too big" и "hot method too big" - the message is printed by compiler С2 if the called method size exceeds then MaxInlineSize (35) or FreqInlineSize (325) respectively. So both messages mean the same thing, but at different levels of compilation.

Execution result:

    130   90       3       com.rkdeep.App::inline2 (4 bytes)
@ 0 com.rkdeep.App::inline3 (4 bytes) inline
@ 0 com.rkdeep.App::inline4 (2 bytes) inline
130 88 1 com.rkdeep.App::inline4 (2 bytes)
130 89 3 com.rkdeep.App::inline1 (4 bytes)
@ 0 com.rkdeep.App::inline2 (4 bytes) inline
@ 0 com.rkdeep.App::inline3 130 92 4 com.rkdeep.App::inline2 (4 bytes)
(4 bytes) inline
@ 0 com.rkdeep.App::inline4 (2 bytes) inline
130 91 2 com.rkdeep.App::inline3 (4 bytes)
@ 0 com.rkdeep.App::inline4 (2 bytes) inline
131 90 3 com.rkdeep.App::inline2 (4 bytes) made not entrant
@ 0 com.rkdeep.App::inline3 (4 bytes) inline (hot)
@ 0 com.rkdeep.App::inline4 (2 bytes) inline (hot)
131 93 4 com.rkdeep.App::inline1 (4 bytes)
131 89 3 com.rkdeep.App::inline1 (4 bytes) made not entrant
@ 0 com.rkdeep.App::inline2 (4 bytes) inline (hot)
@ 0 com.rkdeep.App::inline3 (4 bytes) inline (hot)
@ 0 com.rkdeep.App::inline4 (2 bytes) inline (hot)

Reduce the compilation limit by adding the flag -XX:FreqInlineSize=8

    118   95 %     4       com.rkdeep.App::main @ 9 (28 bytes)
118 93 % 3 com.rkdeep.App::main @ 9 (28 bytes) made not entrant
@ 16 com.rkdeep.App::inline1 (4 bytes) inline (hot)
@ 0 com.rkdeep.App::inline2 (10 bytes) too big

As we can see, the method is inlined with the comment "too big" because we set the compilation limit to 8 bytes.

Sometimes there are recommendations to increase the value of the MaxInlineSize flag. Often an aspect is missed if the value of MaxInlineSize is greater than 35, which means that the method will be inlined the first time it is called. However, if the method is called frequently, then its performance has become more important and it will be inlined later. Therefore, the MaxInlineSize flag can reduce the warm-up time required for tests, but will have little effect on applications that run for long periods of time.

References

  1. "Optimizing Java: Practical Techniques for Improving JVM Application Performance" Benjamin J Evans, James Gough, Chris Newland.
  2. "Java Performance : In-depth Advice for Tuning and Programming Java 8, 11, and Beyond" Scott Oaks.
  3. "Java HotSpot VM Options"