最终方法是内联的吗?

发布于 2024-08-10 07:49:03 字数 48 浏览 5 评论 0原文

Java的final方法会自动内联吗?

很多书说可以,很多书说不!

Are Java final methods automatically inlined?

Many books says yes many books says no!!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

稚气少女 2024-08-17 07:49:03

方法的内联由 JIT 编译器执行,而不是 javac。

现代 JIT 编译器(包括 Hotspot)通常甚至可以内联非最终方法,并在必要时适当地“撤消”优化。他们基本上非常聪明。

简而言之:这完全取决于虚拟机。在我看来,你应该根据什么产生最干净的代码而不是性能来确定你的方法是否最终确定。我个人是“为继承而设计或禁止它”的粉丝,但这是一个不同的讨论:)

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 :)

音栖息无 2024-08-17 07:49:03

有趣的问题促使我进一步研究它。我发现了 2 条有趣的评论 -

  • 1 评论 自动
    内联是一个错误:

与许多人的暗示相反
提示,声明为final的方法不能
由编译器安全地内联,
因为该方法可以有一个
运行时的非最终声明。

要了解原因,假设编译器看起来
A 类和 B 类,以及
子类 C 并看到一个最终方法
在 A 中,它内联到 C 中。但是然后
在运行时为 A 加载的版本
和B不同,方法是
A 中不是最终的,并且 B 中被覆盖。
然后C使用了错误的内联
版本。

,更权威一点,来自 sun 白皮书,编写该方法可以保留虚拟,

由于 Java HotSpot VM 可以自动内联绝大多数虚拟方法调用,因此这种性能损失大大减少,在许多情况下甚至完全消除。

这里有更多关于该机制的直接参考

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.

暗恋未遂 2024-08-17 07:49:03

如果您的意思是“它们在编译期间内联吗”,那么不,它们不会。

但是,静态最终字段有时可以由编译器内联,例如基元和字符串。

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.

宁愿没拥抱 2024-08-17 07:49:03

final 更多的是向设计添加语义,而不是告诉编译器或虚拟机内联某些内容。现代虚拟机内联的内容远多于单独的最终方法,因此这不是使用 final 或尝试过多预测运行时优化的好理由。

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.

梦太阳 2024-08-17 07:49:03

我认为这取决于您运行的 JVM 的实现。当然,将方法设置为最终方法允许编译器选择进行此类实现调整。但它是否也可能取决于其他因素 - 例如,如果它是一个巨大的方法,等等......

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.....

明月松间行 2024-08-17 07:49:03

正如 Jon 所说,内联是由 JIT 编译器(在需要时)完成的,而不是在字节码生成级别完成的。另请注意,有时内联可能会导致性能下降,因为它可能会造成相同代码在 cpu l1 缓存中多次出现的情况,从而删除其他代码的空间。 L1 缓存未命中对性能的影响比跳转到缓存函数更大。

相反,常量(又名最终静态变量)被内联。

看这个检查

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));

        }
}

这个翻译过来是:

 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

你可以看到静态函数InlineTest.add已经被invokestatic调用了多次

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

哆兒滾 2024-08-17 07:49:03

Hotspot 决定是否内联是极其复杂的,依赖于过多的考虑,但我不认为该方法是否被标记为“final”是其中之一。原因是它已经知道该方法的多个实现是否已加载到VM中,因此知道是否允许这样的实现是无关紧要的。

无论如何,内联的只是非常小且简单的方法,甚至不是所有这些方法。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文