java8默认接口方法能被jit编译器内联吗?

ukdjmx9f  于 2021-06-29  发布在  Java
关注(0)|答案(2)|浏览(378)

我在jitwatch中为一段代码运行的基准测试中分析了热点日志,发现很多方法调用由于“无静态绑定”而没有内联。这些似乎只在调用默认接口方法时发生。
我的问题是默认接口方法是否阻止jit编译器内联它们的调用?

  1. interface A {
  2. default double a() {
  3. return Math.random();
  4. }
  5. }
  6. interface B extends A {
  7. default double b() {
  8. return a();
  9. }
  10. }
  11. class C implements B {
  12. public double c() {
  13. double c = 0;
  14. for (int i = 0; i < 1_000_000; ++i) {
  15. c += b();
  16. }
  17. return c;
  18. }
  19. public static void main(String[] args) {
  20. System.out.println(new C().c());
  21. }
  22. }

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

jdgnovmf

jdgnovmf1#

eugene的例子显示默认方法可以内联。
事实上,我认为内联的标准应该与任何其他非静态方法相同。
要内联的代码的大小必须小于可调阈值。
该方法不能被类或接口的任何(当前加载的)子类中的方法重写。
在您的示例中,我认为内联应该是可能的,假设这是示例中涉及的所有代码。
但是,这里使用的特定jit可能有其他限制。例如,一个调用另一个默认方法的默认方法可能是一个边缘情况,这种情况非常罕见,不值得支持。另一种可能的解释是c1编译器没有进行深入的单态分派分析/优化。
另一方面,这可能是过早的优化。。。除非您的性能评测已经在代码中确定了一个特定的热点,在那里内联可能会产生显著的影响。通常情况下,最好的策略是让编译器处理这个问题。如果您对代码进行微优化,以便为给定的java版本提供最佳性能,那么当您更改到较新的版本时,很有可能需要重做这项工作。

wooyq4lh

wooyq4lh2#

它是内联的。举个例子:

  1. public class DefaultInline {
  2. public static void main(String[] args) {
  3. System.out.println(callMe());
  4. }
  5. static int callMe(){
  6. A instance = new A(){};
  7. int x = 0;
  8. for (int i = 0; i < 1_000_000; ++i) {
  9. x += (int)instance.myRandom();
  10. }
  11. return x;
  12. }
  13. interface A {
  14. default double myRandom() {
  15. return Math.random();
  16. }
  17. }
  18. }

运行时使用:

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

看到一行有:

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

至于“无静态绑定”,它在这里出现,请注意,这是在 C1 . 因为调用方法 myRandom 编译 invokeInterface (您可以在上面查看方法的类型 C1 将内联), C1 compiler 不会内联它(据我所知的代码),但 C2 威尔。

展开查看全部

相关问题