【问题标题】:why does optimized virtual calls point to same address in hotspot jit assembly?为什么优化的虚拟调用在热点 jit 程序集中指向相同的地址?
【发布时间】:2021-07-17 08:12:27
【问题描述】:

这里是an article介绍虚调用的jit优化策略。

但令我惊讶的是,所有优化的虚拟调用都使用相同的地址,比如文章中的callq 0x000000011418ea00

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

【问题讨论】:

  • 文章不是已经说明了吗?虚拟调用需要查找 VMT,该 VMT 由正在调用其方法的对象的实例所指向。所有对象的查找代码都是相同的。或者至少它可以处理不同的对象实例。
  • @MargaretBloom 我有两个问题。首先是查找代码是如何知道当前调用方法的索引的,我没有找到任何地方设置这样的索引。其次,如果该地址后面的代码只是进行 VMT 查找,为什么称为“优化虚拟调用”?或者更确切地说,优化是什么?
  • 你没有在哪里找到它?这就像 C++ vtable。每个对象实例都是用一个指针列表创建的。派生对象使用所有基类指针,但会覆盖那些对应于被覆盖方法的指针。索引由方法固定,改变的是指针值。我简要阅读了那篇文章,优化是内联方法并完全跳过 VMT 查找。
  • @MargaretBloom 跳过 VMT 查找是什么意思?实际上我很好奇它是如何跳过 VMT 查找的,因为我可以从程序集中学到的只是所有函数调用都指向同一个地址。
  • 我只浏览了那篇文章,但据我所见,当一个方法被优化时,它被内联并且virtualcall 字节码被完全发出。您正在查看未优化方法的汇编代码。优化后不会有virtualcall及其相关的汇编代码。如果您自己反汇编这些类,可能会更容易看到这一点,查看上下文陈述不佳的代码摘录会令人困惑:)

标签: java assembly jit hotspot


【解决方案1】:

我的理解是,在后台还有另一个优化,identical code folding

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 代码实例。

附言

我认为这种优化更多是随机的副作用,并不是作者的本意。在他的文章的下一个版本中,要求他对CustObj2 类中的代码进行更多更改可能是有意义的。

【讨论】:

  • 有趣的答案。但是在我的实验中,所有在反汇编中标记为optimized virtual_call 的方法都指向同一个地址,尽管它们之间存在具体差异,这与您的假设相冲突。
  • 我怀疑幕后的东西是一种通用的调度方法,但我暂时没有证据证明这一点。
猜你喜欢
  • 2011-12-12
  • 1970-01-01
  • 2011-12-12
  • 2016-10-05
  • 2022-11-23
  • 1970-01-01
  • 2016-10-06
  • 2016-02-08
  • 2011-05-15
相关资源
最近更新 更多