基于方法的 JIT 编译器如何处理 class 字段成员

How method-based JIT compiler handles class field members

即时 (JIT) 编译器是指在程序运行时将代码转换为本机代码的编译器。通常,它会将字节码转换为 Java 编程语言的机器码。

JIT编译器一般有两种:method-based jit和trace-based jit。前者大多是profile runtime程序,只选择hot方式编译。

我的问题是 JIT 编译器如何处理 class 字段成员? _b 引用堆上新创建的 Java 对象。那么jit编译选择方法'test(string)V'时,JIT编译器如何翻译getField指令呢?这里生成的本地代码和字节码之间有没有jump来回?

class A{
   MyObject _b = new MyObject(..);

   public void test(String ars){
       aload 0 
       getField A::_b MyObject
       invokvirtual MyObject sayHello (String)V
       ...
   }
}

HotSpot 虚拟机维护着一堆方法帧。该堆栈通常包含编译帧和解释帧,具体取决于执行代码的 "hotness"。

可以通过 JIT 编译代码以任何方式执行对象分配,只要字节代码程序的语义不变即可。如果编译器可以证明该对象从未被使用过,它甚至可以完全避免分配,因为这不会改变提到的语义。

您质疑“JIT 编译器如何处理 class 字段成员”,包含两个错误的假设。首先,没有我们可以谈论的“the”JIT 编译器,有很多不同的现有 JIT 编译器/优化器,即使我们将自己限制在 Oracle 的 JVM 的行为上,我们不能一概而论。其次,没有单独处理单个功能,尤其是像字段访问这样简单的功能。

不清楚为什么您认为在执行 getfield 指令期间编译代码和解释代码之间存在交互。无论JVM如何实现对象引用,它们都是一种,读取字段_b的值就意味着从内存中读取一个值。向字段写入值,即对新创建对象的引用,发生在 A 的构造函数中,而不是在读取字段时。 code 的状态与该内存访问完全无关。

当谈到invokvirtual指令时,它的执行可能意味着从解释模式到编译代码的改变,反之亦然。但是,同样,这与前面的 getfield 指令无关。如果目标对象引用是 aload 指令或前面的方法调用或方法内部的新分配的结果,那也不会有什么不同。它仅取决于被调用方法的代码状态,即 MyObject.sayHello 或子 class.

的覆盖方法

一样,当该方法正在 compiled/optimized 时,生成的代码不太可能在主流 CPU 上对堆栈建模。生成的本机代码不会将引用推送到堆栈,而只是直接读取字段值并将其用于后续调用。该引用可能会暂时存储在 CPU 寄存器中,但如果目标 CPU 可以处理此类间接寻址,则读取操作和调用也可能会融合到一条指令中。也有可能 sayHello 的代码被内联了,优化器的整体结果完全没有原始代码的记忆。