Java 8个默认接口方法能否被JIT编译器内联?

Can Java 8 default interface methods be inlined by the JIT compiler?

我正在为 JITWatch 中的一段代码 运行 分析 HotSpot 日志以获得基准,并注意到许多方法调用由于“无静态绑定”而未被内联。这些似乎只发生在调用默认接口方法时。

我的问题是默认接口方法是否会阻止 JIT 编译器内联它们的调用?

interface A {
    default double a() {
        return Math.random();
    }
}

interface B extends A {
    default double b() {
        return a();
    }
}

class C implements B {
    public double c() {
        double c = 0;
        for (int i = 0; i < 1_000_000; ++i) {
            c += b();
        }
        return c;
    }

    public static void main(String[] args) {
        System.out.println(new C().c());
    }
}

在 JITWatch 中进一步检查后,这个问题似乎与调用其他默认接口方法的默认接口方法有关。鉴于“无静态绑定”消息,这将更有意义。

它是内联的。这是一个例子:

public class DefaultInline {

    public static void main(String[] args) {
        System.out.println(callMe());
    }

    static int callMe(){
        A instance = new A(){};
        int x = 0;
        for (int i = 0; i < 1_000_000; ++i) {
            x += (int)instance.myRandom();
        }
        return x;
    }

    interface A {
        default double myRandom() {
            return Math.random();
        }
    }

}

运行 它与:

java -XX:+UnlockDiagnosticVMOptions 
     -XX:+PrintInlining 
     -XX:CICompilerCount=2 
     DefaultInline.java

并看到一行包含:

@ 20   zero.x.so.DefaultInline$A::myRandom (4 bytes)   inline

至于“无静态绑定”,它存在于 here, notice that this is in C1. Because calling the method myRandom compiles with invokeInterface (and you can look above 方法的类型中 C1 将内联),C1 compiler 不会将其内联(至于我理解代码),但是 C2 会。

Eugene 的示例表明可以内联默认方法。

事实上,我认为内联的标准应该与任何其他非静态方法相同。

  • 要内联的代码大小必须小于可调阈值。
  • 该方法不得被 class 或接口的任何(当前加载的)子class 中的方法覆盖。

在您的示例中,我认为内联应该是可能的,假设这是示例中涉及的所有代码。

但是,在此处使用的特定 JIT 中,/ 可能存在其他限制。例如,调用另一个默认方法的默认方法可能是一种边缘情况,这种情况非常罕见,因此被认为不值得支持。另一种可能的解释是 C1 编译器没有进行深度 单态调度 分析/优化。

另一方面,这 可能是 过早的优化...除非您的性能分析已确定代码中的特定热点,其中内联可能会产生重大影响区别。通常,最好的策略是将其留给编译器。如果您对代码进行微优化以针对给定的 Java 版本提供最佳性能,则很有可能在更改为较新版本时需要重做这些工作。