JVM的内联和继承的交互



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

假设我们有一个接口IFace,它有许多实现AZ。还假设我们有一个抽象类Foo,它包含一个类型为IFace的字段,并在其唯一的方法(即final(中调用该字段上的方法,并且我们有许多Foo的子类,所有这些子类都不起作用,如下所示(为简洁起见,省略了构造函数(:

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的所有实例实际上都具有相应类型Xfield值。进一步假设我们在一个非常热门的地方有这样的代码:

for (final Foo foo : foos) {
    foo.doAThing();
}

其中foos是类型List<Foo>,是大的,并且包含Foo的所有子类的随机且大致均匀的分布。

foo.doAThing()调用站点非常变形,因此不会发生内联。但是,当确定field.act()是否可以在doAThing中内联时,是按子类进行类型分析,还是仅针对整个Foo进行类型分析?如果每个子类,我们将有26个版本的doAThing,每个版本对field.act()的调用都是单态的且可内联的,但如果我们只有一个,则对field.act()的调用将是超态的且不可内联的。

(我知道这个问题的答案可能是JDK特定的,所以我特别询问OpenJDK的最新版本(

按方法收集配置文件数据。假设doAThing()在您的案例中是最终的,那么所有子类只有一个方法。因此,如果CCD_ 23实际上被多次调用,则CCD_ 22的配置文件可能会被其他Foo破坏。

目前(从JDK8u60开始(类型配置文件没有考虑上下文
有一个错误报告JDK-8015416在将来的某个时候修复了这个问题。

评测是分析程序运行时行为的行为,而不是分析其代码。因此,它不是"每个子类完成",也不是"对于Foo作为一个整体",因为两者都没有意义。

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

这个分析已经提供了两个必要的信息,是否有一个主要的调用目标,如果没有单态行为,以及它是哪种方法。因此,存在多少替代实现并不重要,只要它们很少被调用。

基于这些信息进行优化还意味着JVM会注意检测程序的行为是否发生了变化,并且优化后的代码必须被取消优化,如果不是被不同的优化变体所取代的话。

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

最新更新