Java 包含私有调用的内联方法
Java inline method that contains private calls
假设 jvm 将 b.foo() 方法识别为热方法,它会尝试内联它吗?因为如果这样做,调用指令 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:错误的调用特殊指令:当前 class 不可分配给引用 class
编辑 2:
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
在Java中,优化通常在JVM 级别完成。在运行时,JVM 执行一些 "complicated" 分析以确定要内联哪些方法。它在内联方面可以很积极,Hotspot JVM 实际上可以内联非最终方法。
java 编译器几乎从不内联任何方法调用(JVM 在运行时完成所有这些)。他们做内联编译时间常量(例如最终静态原始值)。但不是方法。
更多资源:
- 开头有源码
- java 编译器将其转换为字节码
- jvm 读取字节码、验证其完整性并对其进行解释。 "hot spots" 的字节码(频繁执行的代码块)被即时编译到 CPU 的指令集中 JVM 是 运行 on
在 Java 中,大多数优化被推迟到步骤 3 中的 JIT 编译器,以从程序执行期间收集的统计信息中获益。这包括内联。
在验证期间、在解释字节码之前以及在 JIT 编译之前检查 private
等访问修饰符。由于已经检查了访问修饰符,因此 JIT 完全不知道它们的存在,并且内联私有方法不会造成任何困难。
顺便说一句,如果你想检查 JIT 做了什么,你可以使用热点的 -XX:+PrintAssembly
将机器代码翻译回汇编代码以供你检查。
假设 jvm 将 b.foo() 方法识别为热方法,它会尝试内联它吗?因为如果这样做,调用指令 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:错误的调用特殊指令:当前 class 不可分配给引用 class
编辑 2:
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
在Java中,优化通常在JVM 级别完成。在运行时,JVM 执行一些 "complicated" 分析以确定要内联哪些方法。它在内联方面可以很积极,Hotspot JVM 实际上可以内联非最终方法。
java 编译器几乎从不内联任何方法调用(JVM 在运行时完成所有这些)。他们做内联编译时间常量(例如最终静态原始值)。但不是方法。
更多资源:
- 开头有源码
- java 编译器将其转换为字节码
- jvm 读取字节码、验证其完整性并对其进行解释。 "hot spots" 的字节码(频繁执行的代码块)被即时编译到 CPU 的指令集中 JVM 是 运行 on
在 Java 中,大多数优化被推迟到步骤 3 中的 JIT 编译器,以从程序执行期间收集的统计信息中获益。这包括内联。
在验证期间、在解释字节码之前以及在 JIT 编译之前检查 private
等访问修饰符。由于已经检查了访问修饰符,因此 JIT 完全不知道它们的存在,并且内联私有方法不会造成任何困难。
顺便说一句,如果你想检查 JIT 做了什么,你可以使用热点的 -XX:+PrintAssembly
将机器代码翻译回汇编代码以供你检查。