Java final方法是否自动内联?
许多书都说很多书说没有!!!
答案 0 :(得分:9)
方法的内联由JIT编译器执行,而不是javac。
现代JIT编译器(包括Hotspot)通常可以内联甚至非最终方法,必要时“撤消”优化。他们基本上都很聪明。
简而言之:它完全取决于VM。在我看来,你应该根据产生最干净代码而不是性能的方法来使你的方法成为最终方法。我个人是“设计继承或禁止它”的粉丝,但这是一个不同的讨论:)
答案 1 :(得分:4)
有趣的问题,促使我进一步研究。我发现了两个有趣的评论 -
与许多人的含义相反 提示,声明为final的方法不能 由编译器安全地内联, 因为该方法可以有一个 运行时的非最终声明。
要了解原因,请假设编译器看起来 在A类和B类,和 子类C并看到最终方法 在A中,它内联到C.但是然后 在运行时为A加载的版本 和B是不同的,方法是 在A中没有最终,在B中被覆盖 然后C使用错误的内联 版。 Ť
并且,从sun whitepaper开始,更具权威性,写出方法可以保持虚拟,
由于Java HotSpot VM可以自动内联绝大多数虚拟方法调用,因此这种性能损失会大大降低,并且在很多情况下会完全消除。
这个机制更多direct reference。
答案 2 :(得分:2)
如果你的意思是“他们在编译过程中被内联”,那么不,他们不会。
但是,静态最终字段有时可以由编译器内联,例如基元和字符串。
答案 3 :(得分:2)
final
更多的是为设计添加语义而不是告诉编译器或VM内联某些内容。现代虚拟机内联的内容远远超过最终方法,所以这不是使用final
或尝试过多预测运行时优化的好理由。
答案 4 :(得分:1)
我认为这取决于您运行的JVM的实现。当然,使方法最终允许编译器选择进行这样的实现调整。但是否这样做也可能取决于其他因素 - 例如如果它是一个巨大的方法等等......
答案 5 :(得分:0)
Hotspot决定是否内联是非常复杂的,取决于过多的考虑因素,但我不认为该方法是否标记为“最终”是其中之一。原因是它已经知道是否已经在VM中加载了该方法的多个实现,因此也知道是否允许这样的实现是无关紧要的。
在任何情况下,它都只是非常小而简单的内联方法,甚至不是所有这些方法。
答案 6 :(得分:0)
正如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
多次调用