Post

Method inlining in Java

Describe inline approach for runtime JVM optimisation.

Method inlining in Java

What method inlining is?

Inlining is an optimization technique where the JVM replaces a method call with the actual body of the called method. Normally, when a method is called, the JVM allocates a new stack frame for the method on the call stack, and once the method completes, control returns to the calling method. By inlining, the JVM avoids the overhead of creating and managing a new stack frame, which improves performance. Docs Version Dropdown

Which flags control inlining?

The JVM option -XX:+PrintFlagsFinal displays the active JVM flags when the application starts. Among these flags, several are particularly relevant for understanding method inlining:

  1. CompileThreshold:
    • Specifies the number of method calls before the method is considered “hot.”
    • Once a method is “hot,” it becomes a candidate for just-in-time (JIT) compilation and optimization, including inlining.
  2. MaxInlineLevel:
    • Defines the maximum depth of the method call chain that can be inlined.
    • Default value: 9.
  3. MaxInlineSize:
    • Specifies the maximum size of a method (in bytecode) that can be inlined.
    • Default value: 35 bytes.
    • If a method is not considered “hot” and its bytecode size exceeds 35 bytes, it will not be inlined.
  4. FreqInlineSize:
    • Determines the maximum size of a “hot” method that can be inlined.
    • The default value depends on the platform. For example, on my laptop, the default is 325 bytes.

How does JIT consider a method for inlining?

The JIT compiler decides whether to inline a method based on two primary factors:

  • How many times the method has been called.
  • The size of the method in bytecode. The decision to inline a method depends on both its size and how “hot” it is. The JVM determines whether a method is “hot” using its internal logic, which is not directly configurable.

If a method is “hot” because it has been called frequently, it will only be inlined if its bytecode size is less than 325 bytes. Conversely, if a method is not considered “hot,” it may still be inlined if its bytecode size is less than 35 bytes. The JIT compiler is more likely to inline static, private, or final methods, as their implementations are fixed and known at compile time. While public methods are also candidates for inlining, the JVM will only inline them if it can determine that there is a single implementation of the method (e.g., it is not overridden).

Example of inlining

Consider listing below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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;
    }
}
1
2
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”: This message is printed by the C1 compiler when the size of the called method exceeds the value specified by the JVM parameter -XX:MaxInlineSize, which defaults to 35 bytes.
  2. “too big” and “hot method too big”: These messages are printed by the C2 compiler when the size of the called method exceeds:
    • MaxInlineSize (35 bytes) for non-hot methods.
    • FreqInlineSize (325 bytes, platform-dependent) for hot methods. Both messages indicate the same issue—method size exceeding the inlining limit—but occur at different levels of compilation.

Execution result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    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

1
2
3
4
    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.

It is sometimes recommended to increase the value of the MaxInlineSize flag. However, an important aspect is often overlooked: if the MaxInlineSize value exceeds 35 bytes, a method will be inlined on its first call. On the other hand, if the method is called frequently, its performance becomes more significant, and it will be inlined later.

Increasing the MaxInlineSize flag can help reduce the warm-up time required for tests or short-lived applications, but it typically has little impact on long-running applications, where hot methods are optimized over 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”
This post is licensed under CC BY 4.0 by the author.

Trending Tags