Question

Are Java final methods automatically inlined?

Many books says yes many books says no!!!

Was it helpful?

Solution

Interesting question, prompted me to look into it further. 2 interesting remarks I found -

  • 1 comment that automatic inlining is a bug:

Contrary to the implication of many tips, methods declared as final cannot be safely inlined by the compiler, because the method could have a non-final declaration at runtime.

To see why, suppose the compiler looks at class A and subclass B, and sub-subclass C and sees a final method in A which it inlines into C. But then at runtime the versions loaded for A and B are different and the method is not final in A, and overridden in B. Then C uses the incorrectly inlined version. T

And, a bit more authoritatively, from a sun whitepaper, writing that methods can be left virtual,

Because the Java HotSpot VM can automatically inline the vast majority of virtual method invocations, this performance penalty is dramatically reduced, and in many cases, eliminated altogether.

Here's a more direct reference on the mechanism.

OTHER TIPS

Inlining of methods is performed by the JIT compiler, not javac.

Modern JIT compilers (including Hotspot) can often inline even non-final methods, "undoing" the optimisation appropriately if necessary. They're basically scarily clever.

In short: it entirely depends on the VM. In my opinion, you should make your methods final or not based on what produces the cleanest code rather than performance. I'm personally a fan of "design for inheritance or prohibit it" but that's a different discussion :)

If you mean "do they get inlined during compilation", then no, they won't.

However, static final fields sometimes can be inlined by the compiler, for example primitives and Strings.

final is more about adding semantics to a design than to tell a compiler or VM to inline something. Modern VMs inline much more than final methods alone, so this is not a good reason to use final or try to predict too much about runtime optimizations.

I think that depends on which implementation of the JVM you are running on. Certainly, making a method final allows the compiler the option of making such an implementation tweak. But whether it does or not may also depend on other factors - e.g. what if its a huge method, etc.....

Hotspot's decision of whether to inline is incredibly complicated, dependent on a plethora of considerations, but I don't think whether the method is marked "final" is one of them. The reason is that it is already aware of whether multiple implementations of that method have been loaded in the VM, so it's irrelevant to also know whether such implementations are allowed.

In any case, it's only going to be very small and simple methods that get inlined, and not even all of those.

As Jon said the inlining is done (when needed) by the JIT compiler not at bytecode generation level. Note also that sometimes inlining can result in a performance degradation because it could create a situation where the same code is present multiple times in the cpu l1 cache, removing space for other code. L1 cache misses can impact the performance more than a jump to a cached function.

Constants (aka final static var) are inlined instead.

See this to check

public class InlineTest {
    final static int add(int x, int y) {
        return x + y;
    } 
}


public class Main {

        static final int DIVISOR = 7;

        static void main(String[] args){
            final int a = new Integer(args[0]);
            final int b = new Integer(args[1]);

            if (InlineTest.add(a, b) % DIVISOR == 0)
                System.exit(InlineTest.add(a, b));

            System.out.print("The sum is " + InlineTest.add(a, b));

        }
}

This is translated in:

 0 new #2 <java/lang/Integer>
 3 dup
 4 aload_0
 5 iconst_0
 6 aaload
 7 invokespecial #3 <java/lang/Integer/<init>(Ljava/lang/String;)V>
10 invokevirtual #4 <java/lang/Integer/intValue()I>
13 istore_1
14 new #2 <java/lang/Integer>
17 dup
18 aload_0
19 iconst_1
20 aaload
21 invokespecial #3 <java/lang/Integer/<init>(Ljava/lang/String;)V>
24 invokevirtual #4 <java/lang/Integer/intValue()I>
27 istore_2
28 iload_1
29 iload_2
30 invokestatic #5 <com/gamasoft/InlineTest/add(II)I>
33 bipush 7
35 irem
36 ifne 47 (+11)
39 iload_1
40 iload_2
41 invokestatic #5 <com/gamasoft/InlineTest/add(II)I>
44 invokestatic #7 <java/lang/System/exit(I)V>
47 getstatic #8 <java/lang/System/out Ljava/io/PrintStream;>
50 new #9 <java/lang/StringBuilder>
53 dup
54 invokespecial #10 <java/lang/StringBuilder/<init>()V>
57 ldc #11 <The sum is >
59 invokevirtual #12 <java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;>
62 iload_1
63 iload_2
64 invokestatic #5 <com/gamasoft/InlineTest/add(II)I>
67 invokevirtual #13 <java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;>
70 invokevirtual #14 <java/lang/StringBuilder/toString()Ljava/lang/String;>
73 invokevirtual #15 <java/io/PrintStream/print(Ljava/lang/String;)V>
76 return

You can see that static function InlineTest.add has been called multiple times with invokestatic

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top