Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 最终的方法是内联的吗?_Java_Methods_Inline_Final - Fatal编程技术网

Java 最终的方法是内联的吗?

Java 最终的方法是内联的吗?,java,methods,inline,final,Java,Methods,Inline,Final,Java final方法是否自动内联 很多书都说是,很多书都说不是 如果您的意思是“它们在编译期间内联了吗”,那么不,它们不会 但是,静态final字段有时可以由编译器内联,例如原语和字符串。我认为这取决于您运行的JVM的哪个实现。当然,使方法成为final允许编译器选择进行这样的实现调整。但是,是否这样做也可能取决于其他因素,例如,如果它是一个巨大的方法,等等……方法的内联是由JIT编译器执行的,而不是javac 现代JIT编译器(包括Hotspot)通常可以内联甚至非最终方法,必要时适当地“

Java final方法是否自动内联

很多书都说是,很多书都说不是

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


但是,静态final字段有时可以由编译器内联,例如原语和字符串。

我认为这取决于您运行的JVM的哪个实现。当然,使方法成为final允许编译器选择进行这样的实现调整。但是,是否这样做也可能取决于其他因素,例如,如果它是一个巨大的方法,等等……

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

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


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

final
更多的是为设计添加语义,而不是告诉编译器或VM内联一些东西。现代虚拟机内联的不仅仅是final方法,所以这不是使用
final
或试图预测太多运行时优化的好理由。

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

  • 那是自动的吗 内联是一个bug:
与许多人的暗示相反 提示:声明为final的方法不能 由编译器安全地内联, 因为这个方法可能有一个 运行时的非最终声明

要了解原因,假设编译器 在A类和B子类,以及 子类C,并查看最终的方法 在A中,它内联到C,但是 在运行时,为一个 和B不同,方法不同 在A中不是最终的,在B中被重写。 然后C使用了不正确的内联 版本T

更具权威性的是,在一篇文章中,方法可以是虚拟的

因为JavaHotSpot虚拟机可以自动内联绝大多数虚拟方法调用,所以这种性能损失会显著降低,并且在许多情况下完全消除


这里有一个更详细的机制。

Hotspot决定是否内联是非常复杂的,取决于太多的考虑因素,但我不认为该方法是否标记为“final”是其中之一。原因是它已经知道该方法的多个实现是否已加载到VM中,因此也不必知道是否允许这样的实现


在任何情况下,只有非常小和简单的方法可以内联,甚至不是所有的方法。

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

常量(也称为最终静态变量)是内联的

看看这个来检查一下

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
0新#2
3次重复
4阿洛德0
5 iconst_0
6安培负载
7特别是#3
10调用虚拟设备#4
13史前1
14新#2
17次重复
18 aload_0
19 iconst_1
20安培负载
21特别是#3
24调用虚拟设备4
27史努比2
28 iload_1
29 iload_2
30房地产#5
33双推7
35伊雷姆
36 ifne 47(+11)
39 iload_1
40 iload_2
41房地产#5
44房地产#7
47 getstatic#8
50新#9
53次重复
54特别是#10
57最不发达国家11
59虚拟的#12
62 iload_1
63 iload_2
64房地产#5
67虚拟的#13
70虚拟的#14
73虚拟的#15
76返回

您可以看到,InlineTest.add静态函数已通过调用invokestatic被多次调用

为什么要在意?(为什么这样至少有15个字符?@erikkallen-这就是“”的含义。:-)如果热点发现发生了这种情况,则内联将撤消。我相信它只是在所有受影响的类上重新启动整个JIT过程。OpenJDK做的和HotSpot做的一样吗?这不是以前“javac-O”做的吗?@Thorbjørn:不确定。。。我不这么认为,但我可能大错特错了。我想你的意思是说“javac编译”,因为另一个问题也是编译。