java - JVM JIT 能否专门化子类中的非覆盖方法?

标签 java performance inheritance optimization jvm

好吧,这个标题并不能理解这个想法,但基本上我的意思是在类 Base 中给定一些方法 m(),这不是在某些子类 Derived 中重写的是当前 JVM 中的 JIT 编译器1 能够“专门化”0 m() 无论如何,当它有意义时,或者派生谁继承并且不覆盖 Base.m() 共享相同的编译代码?

这种特殊化是有道理的,派生 类定义了使 m() 更简单的东西。例如,为了便于讨论,假设 m() 调用另一个成员函数 n() 并在派生类中 n()被定义为当 n() 被内联到 m() 时,后者被大大简化。

具体的,考虑下面类中的两个非抽象方法(都是m()类型的方法,而抽象方法是对应的n() 方法):

public class Base {

  abstract int divisor();
  abstract boolean isSomethingEnabled();

  int divide(int p) {
    return p / divisor();
  }

  Object doSomething() {
    if (isSomethingEnabled()) {
      return slowFunction();
    } else {
      return null;
  }
}

两者都依赖于抽象方法。假设您现在有一个像这样的 Derived:

public class Derived extends Base {

  final int divisor() {
    return 2;
  }

  final boolean isSomethingEnabled() {
    return false;
  }
}

现在 divide()doSomething() 方法的有效行为非常简单,divide 不是任意数字的完整除法,而是可以通过位操作完成的简单减半。 doSomething() 方法总是返回 false。我假设当 JIT 编译 divide()doSomething() 时,如果 Derived 是>只有子类,一切都很好:两个抽象调用(目前)只存在一种可能的实现,CHA 将启动并内联唯一可能的实现,一切都很好。

但是,在存在其他派生类的更一般情况下,我不清楚 JVM 是否只会编译 一个2 版本的方法Base 带有对抽象方法的 invokevirtual 调用,或者如果它足够聪明地说,“嘿,即使 Derived 没有覆盖 divisor() 我应该专门为它编译一个版本,因为它会简单得多。”

当然,即使没有专门的重新编译,积极的内联通常也能正常工作(即,当您在已知的类上调用 divide() 时,甚至可能是 Derived,无论如何,内联可能会为您提供良好的实现,但同样,在很多情况下,这种内联并未完成。


0 我的专长我不是指任何特定的东西,只是编译适用于某些受限领域的另一个版本的函数,就像说内联是一种形式专门化到特定的调用站点,或者以与大多数函数在某种程度上专门化到当前上下文(例如,加载的类、关于空性的假设等)相同的方式。

1特别是,当有人说“JVM 可以废话吗?”时一个通常是在谈论 Hotspot,我也主要在 Hotspot 中,但也在讨论其他 JVM 是否也可以做到这一点。

2好的,当然,您可能有一个函数的多个版本,用于堆栈替换、不同的编译器级别、发生去优化等...

最佳答案

  1. HotSpot JVM 至多有一个当前的、进入的 版本的编译方法。从源代码中 Methodnmethod 实体之间的一对一关系可以明显看出这一点。但是,可能有多个未进入的先前版本(例如,在较低层编译的 nmethod 和 OSR stub )。
  2. 此单一编译版本通常根据运行时分析针对最常见的情况进行优化。例如,在分析 Base.doSomething() 期间,JIT 发现 isSomethingEnabled() 总是在 Derived 实例上调用(即使有更多的子类),它将优化快速案例的调用,为慢速案例留下一个不常见的陷阱。优化后 doSomething() 看起来像

        if (this.getClass() != Derived.class) {
            uncommon_trap();  // this causes deoptimization
        }
        return false;
    

  1. 个人资料数据是针对每个分支机构和每个调用站点单独收集的。这使得可以针对一个接收器优化(专门化)方法的一部分,并针对不同的接收器优化(专门化)另一部分。
  2. 如果在分析过程中检测到两个不同的接收者,JIT 可以内联两个受类型检查保护的被调用者。
  3. 将使用 vtable 查找编译具有两个以上接收者的虚拟调用。

要查看方法配置文件数据,请使用 JVM 调试版本中可用的 -XX:+PrintMethodData 选项。

关于java - JVM JIT 能否专门化子类中的非覆盖方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42352282/

相关文章:

php - SQL Count + 3 join + multiple where in one query - 如何编写 propen IF?

c++ - 基类私有(private)成员属于派生类的哪一部分?

c++ - 继承破坏了 C++ 中的多态性?

C++ 存储变量和继承

javascript - 选择选项后禁用输入字段

java - LibGdx 如何重复背景?

java - Bukkit 自定义库存刷怪蛋

java - 无法将 Spring-Batch 代理类转换为类?

performance - Matlab代码的向量化

java - 如何在下面使用带有 Apache httpclient 的 jersey-client?