JIT是否会在一个抽象类的继承方法中内联调用,该方法在所有子类中都是变形的,但对于给定的子类是单形的?
假设我们的接口IFace
包含大量实现A
到Z
。假设我们还有一个抽象类Foo
,它包含IFace
类型的字段,并在其唯一的方法(final
)中调用该字段上的方法,并且我们有许多子Foo
的classess,所有这些都没有做,像这样(为了简洁省略了构造函数):
interface IFace {
void act();
}
final class A extends IFace {
...
}
...
final class Z extends IFace {
...
}
abstract class Foo {
final IFace field;
public final void doAThing() {
field.act();
}
}
final class FooA extends Foo {
}
...
final class FooZ extends Foo {
}
意外地假设' FooX
的所有实例实际上都具有相应类型field
的{{1}}个值。假设我们在一个非常热门的地方有这样的代码:
X
其中for (final Foo foo : foos) {
foo.doAThing();
}
的类型为foos
,很大,并且包含List<Foo>
所有子类的随机且大致均匀的分布。
Foo
呼叫站点非常易变,因此不会发生内联。但是,在确定foo.doAThing()
中是否可以内联field.act()
时,是每个子类完成类型分析还是仅对整个doAThing
进行类型分析?如果每个子类,我们将有26个Foo
版本,每个版本对doAThing
的调用都是单态和可内联的,但如果我们只有一个,则调用{{1}将是变形的而不是内联的。
(我知道答案可能是JDK特定的,所以我特别要求最新版本的OpenJDK)
答案 0 :(得分:3)
按方法收集个人资料数据。鉴于doAThing()
在您的情况下是最终的,所有子类只有一种方法。因此,field.act()
的个人资料可能会被其他Foo破坏,如果doAThing()
实际上被多次调用过。
目前(从JDK8u60开始)类型配置文件不考虑上下文 有一个错误报告JDK-8015416可以在将来的某个时间修复此问题。
答案 1 :(得分:0)
分析是分析程序的运行时行为的行为,而不是其代码。所以它不是“完成每个子类”,而不是“对于Foo
作为一个整体”,因为它们都没有意义。
只是想象JVM定期窥视活动线程的调用堆栈,看看它们实际执行的具体方法以及调用它们的具体方法。堆栈检查甚至可能会增加一些堆栈帧,但通常限制在一定数量的帧中以降低检查成本。
这种分析已经提供了两个必要的信息,即是否存在显性呼叫目标,甚至是单态行为,以及它是哪种方法。因此,只要很少调用它们,存在多少替代实现并不重要。
基于此信息进行优化还意味着JVM会注意检测程序的行为是否发生变化,优化后的代码是否必须进行去优化,如果没有被不同优化的变体替换的话。
JVM知道哪些类曾被实例化(通常甚至不会在首次使用之前加载类)也很有帮助。因此,如果在运行时只有一个interface
的实现类,则将假定一个单态行为,而无需等待探查器证明它。由于JVM知道何时加载新的子类/实现类,因此可以及时进行去优化。