JVM的内联和继承的交互

时间:2015-09-14 17:14:20

标签: java inheritance jvm inline jit

JIT是否会在一个抽象类的继承方法中内联调用,该方法在所有子类中都是变形的,但对于给定的子类是单形的?

假设我们的接口IFace包含大量实现AZ。假设我们还有一个抽象类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)

2 个答案:

答案 0 :(得分:3)

按方法收集个人资料数据。鉴于doAThing()在您的情况下是最终的,所有子类只有一种方法。因此,field.act()的个人资料可能会被其他Foo破坏,如果doAThing()实际上被多次调用过。

目前(从JDK8u60开始)类型配置文件不考虑上下文 有一个错误报告JDK-8015416可以在将来的某个时间修复此问题。

答案 1 :(得分:0)

分析是分析程序的运行时行为的行为,而不是其代码。所以它不是“完成每个子类”,而不是“对于Foo作为一个整体”,因为它们都没有意义。

只是想象JVM定期窥视活动线程的调用堆栈,看看它们实际执行的具体方法以及调用它们的具体方法。堆栈检查甚至可能会增加一些堆栈帧,但通常限制在一定数量的帧中以降低检查成本。

这种分析已经提供了两个必要的信息,即是否存在显性呼叫目标,甚至是单态行为,以及它是哪种方法。因此,只要很少调用它们,存在多少替代实现并不重要。

基于此信息进行优化还意味着JVM会注意检测程序的行为是否发生变化,优化后的代码是否必须进行去优化,如果没有被不同优化的变体替换的话。

JVM知道哪些类曾被实例化(通常甚至不会在首次使用之前加载类)也很有帮助。因此,如果在运行时只有一个interface的实现类,则将假定一个单态行为,而无需等待探查器证明它。由于JVM知道何时加载新的子类/实现类,因此可以及时进行去优化。