让我们从两个简单的类开始:
package com.michaelt.so.supers;
public class Sup {
int methodA(int a, int b) {
return a + b;
}
}
然后
package com.michaelt.so.supers;
public class Sub extends Sup {
@Override
int methodA(int a, int b) {
return super.methodA(a, b);
}
}
编译方法A并查看一个字节码得到:
methodA(II)I
L0
LINENUMBER 6 L0
ALOAD 0
ILOAD 1
ILOAD 2
INVOKESPECIAL com/michaelt/so/supers/Sup.methodA (II)I
IRETURN
L1
LOCALVARIABLE this Lcom/michaelt/so/supers/Sub; L0 L1 0
LOCALVARIABLE a I L0 L1 1
LOCALVARIABLE b I L0 L1 2
MAXSTACK = 3
MAXLOCALS = 3
您可以在那里看到带有invokespecial方法的对象,它针对Sup类methodA()进行查找。
该invokespecial操作码具有以下逻辑:
- 如果C包含一个实例方法的声明,该实例方法的名称和描述符与解析的方法相同,则将调用此方法。查找过程终止。
- 否则,如果C具有超类,则使用C的直接超类递归执行相同的查找过程。要调用的方法是此查找过程的递归调用的结果。
- 否则,引发AbstractMethodError。
在这种情况下,没有实例名称和其类的描述符相同的实例方法,因此第一个项目符号不会触发。但是第二个要点是-有一个超类,它调用了超类的methodA。
编译器不会对此进行内联,并且该类中没有Sup源的副本。
但是故事还没有结束。这只是编译后的代码。一旦代码到达JVM, HotSpot就可以参与其中。
不幸的是,我对此并不太了解,因此我将在此问题上寻求权威,然后转到Java中的内联,据说HotSpot 可以内联方法(甚至是非最终方法)。
转到文档,请注意,如果某个特定的方法调用成为热点而不是每次都不进行查找,则可以内联此信息-有效地将代码从Sup methodA()复制到Sub methodA()。
这是在运行时在内存中完成的,具体取决于应用程序的行为方式以及加快性能所需的优化。
如OpenJDK的HotSpot Internals中所述: “方法通常是内联的。静态,私有,最终和/或“特殊”调用很容易内联。”
如果您深入研究JVM的选项,则会发现一个选项-XX:MaxInlineSize=35
(默认值为35),它是可以内联的最大字节数。我将指出这就是Java喜欢拥有许多小方法的原因-因为它们可以轻松地内联。这些较小的方法在被调用时会变得更快,因为它们可以被内联。而且虽然可以使用该数字并将其增大,但可能会导致其他优化效果降低。(相关的SO问题:HotSpot JIT内联策略,指出了许多其他选择,可以窥探HotSpot正在执行的内联内部)。
因此,不可以-编译时不内联代码。而且,是的-如果性能优化可以保证在运行时很好地内联代码。
我写的有关HotSpot内联的所有内容仅适用于Oracle分发的HotSpot JVM。如果您查看Wikipedia的Java虚拟机列表,不仅有HotSpot,而且这些JVM处理内联的方式可能与上文所述完全不同。Apache Harmony,Dalvik,ART-那里的情况可能有所不同。