【问题标题】:Java inline method that contains private calls包含私有调用的 Java 内联方法
【发布时间】:2018-12-14 21:45:45
【问题描述】:

假设 jvm 将 b.foo() 方法识别为 hot,它会尝试内联它吗?因为如果是这样,调用指令 B.bar() 将被内联在 A.main 方法中,这是禁止的,因为 bar 是私有的。 jvm 在这些情况下做了什么?

任何包含更多详细信息的文档也受到赞赏。谢谢

public class A {

    public static void main(String[] args)
    {
        B b = new B();
        for (int i=0; i<99999; i++)
            b.foo();
    }
}

class B {

    public void foo() {
        bar();
    }

    private void bar() { // do something. (is small method)
    }
}

编辑:我之所以问,是因为当我尝试自己进行此优化时(在字节码中),我得到一个有意义的验证错误:

java.lang.VerifyError: 错误的 invokespecial 指令:当前类不可分配给引用类

EDIT2:

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
  stack=2, locals=5, args_size=1
     0: new           #13                 // class B
     3: dup
     4: invokespecial #14                 // Method B."<init>":()V
     7: astore_1
     8: iconst_0
     9: istore_2
    10: iload_2
    11: ldc           #15                 // int 99999
    13: if_icmpge     30
    16: aload_1
    17: astore        4
    19: aload         4
    21: invokespecial #18                 // Method B.bar:()V // VERIFY ERROR HERE
    24: iinc          2, 1
    27: goto          10
    30: return
  StackMapTable: number_of_entries = 2
    frame_type = 253 /* append */
      offset_delta = 10
      locals = [ class B, int ]
    frame_type = 19 /* same */
  LineNumberTable:
    line 5: 0
    line 6: 8
    line 7: 16
    line 14: 19
    line 15: 24
    line 6: 24
    line 8: 30

【问题讨论】:

  • 为什么你认为禁止内联私有方法?
  • 内联的 foo 方法不是私有的,它是公共的
  • 你能显示你生成的导致VerifyError的字节码吗?
  • @DavidConrad 完成
  • 呃,如果将 bar 内联到 foo 中,并将 foo 内联到 main 中,它会将 makeup bar 的指令插入到 main 中。它不会在 main 中插入 对 bar 的调用

标签: java jvm inline private


【解决方案1】:
  1. 开头,有源码
  2. java 编译器把它变成字节码
  3. jvm 读取字节码,验证其完整性并对其进行解释。 “热点”(经常执行的代码块)的字节码被即时编译到运行 JVM 的 CPU 的指令集中

在 Java 中,大多数优化都推迟到第 3 步中的 JIT 编译器,以便从程序执行期间收集的统计数据中受益。这包括内联。

在验证期间、在解释字节码之前和在 JIT 编译之前检查访问修饰符,例如 private。由于已经检查了访问修饰符,JIT 完全不知道它们的存在,并且内联私有方法不会造成任何困难。

顺便说一句,如果您想检查 JIT 的功能,可以使用热点的 -XX:+PrintAssembly 将机器代码翻译回汇编代码以供您检查。

【讨论】:

  • 实际上,JIT 编译器并非“完全”与访问修饰符无关,因为是否可以覆盖目标方法很重要。在这方面,private 方法更更容易内联,因为它不能被覆盖。尽管在大多数其他情况下,仍有一些方法可以内联可覆盖的方法,例如在 OP 的示例代码中,a)目标是 new 运算符的结果,b)在循环中对同一对象调用该方法c) 无论如何都不存在B 的子类,仅这三点就足以启用内联。
【解决方案2】:

在 Java 中,优化通常在 JVM 级别完成。在运行时,JVM 执行一些“复杂”的分析来确定要内联的方法。它可以积极地内联,Hotspot JVM 实际上可以内联非最终方法。

java 编译器几乎从不内联任何方法调用(JVM 在运行时完成所有这些)。他们做内联编译时间常数(例如最终的静态原始值)。但不是方法。

更多资源:

  1. Article: The Java HotSpot Performance Engine: Method Inlining Example

  2. Wiki: Inlining in OpenJDK,未完全填充,但包含指向有用讨论的链接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-13
    • 1970-01-01
    • 2013-07-12
    • 2020-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多