为什么优化的虚拟调用指向热点jit程序集中的同一地址



这里有一篇文章介绍了虚拟调用的jit优化策略。

但让我感到惊讶的是,所有优化的虚拟调用都使用相同的地址,就像文章中的callq 0x000000011418ea00一样。

因此,我很好奇它在该地址中实际做了什么,以及它如何知道该调用哪个函数,因为所有优化的虚拟调用都指向同一地址。

我的理解是,还有另一个正在进行的优化,即相同的代码折叠。

CustObj::methodCallCustObj2::methodCall的代码非常相似,只是打印字符串不同。JVM编译器因此能够为它们生成相同的代码:

$ javac tmp.java
$ javap -c -constants 'TestVirtualCall2$CustObj.class'
Compiled from "tmp.java"
class TestVirtualCall2$CustObj {
public void methodCall();
Code:
0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
3: lconst_0
4: lcmp
5: ifne          16
8: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
11: ldc           #4                  // String CustObj is very good!
13: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
}
$ javap -c -constants 'TestVirtualCall2$CustObj2.class'
Compiled from "tmp.java"
class TestVirtualCall2$CustObj2 extends TestVirtualCall2$CustObj {
public final void methodCall();
Code:
0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
3: lconst_0
4: lcmp
5: ifne          16
8: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
11: ldc           #4                  // String CustObj2 is very good!
13: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: return
}

注意,即使是字符串加载命令ldc #4也恰好是相同的,因为编译器将字符串放入相应类常量池中的相同位置。

所以JIT肯定只会为两个虚拟方法生成一个x86代码实例。

p.S.

我认为这种优化更多的是一种随机的副作用,并不是作者的意图。在文章的下一次修订中,让他对CustObj2类中的代码做更多的更改可能是有意义的。