JIT 编译代码存放在哪里?

Where does the JIT compiled code reside?

于是就有了这个方法,写在Java:

public void myMethod(int y){
    int x = 5 + y;
    doSomething(x);
}

假设我的应用程序调用了很多次..

当运行这个方法的编译代码在Java虚拟机上时,JVM会先解释这个方法。然后一段时间后,如果我理解正确,它将决定将其编译为机器语言。

至此,

会不会被内存中的机器码覆盖?如果覆盖,大小差异的问题如何解决?如果它被写入内存中的其他地方,加载到内存中的字节码是否会被释放?还有,如果字节码和jit编译后的代码都在内存中,当应用程序再次命中这个方法时,JVM如何决定执行jit编译后的代码而不是字节码?

不,它没有被覆盖,因为两个表示在同一个地方通常没有实际好处。 JVM 字节码只是一段数据。 JIT 发出的代码是本机 CPU 指令流(在某些体系结构中,要求将其显式标记为可执行)。

通常,当需要执行一个新函数时,JIT 编译器读取该函数的字节码,在其他地方分配内存,将等效的本机代码写入该内存,然后 returns 一个函数指针指向新生成的本机代码的入口。

据我所知,The Java® Virtual Machine Specification 没有指定任何内容。
我能找到的对 JIT 的唯一参考是 Chapter 3:

[...] One example of such a translator is a just-in-time (JIT) code generator, which generates platform-specific instructions only after Java Virtual Machine code has been loaded. This chapter does not address issues associated with code generation, only those associated with compiling source code written in the Java programming language to Java Virtual Machine instructions.

所以据我了解,这可以通过不同的实现来完成。

然而,对我来说,包含 java 字节码的内存似乎不太可能被本机 CPU 指令覆盖,因为 CPU 指令在技术上是可执行的,而字节码只是数据,因为它必须被解释。但这并非不可能,只是非常奇怪。

HotSpot JVM 在元空间(或早期版本中的 PermGen)中有一个 Method 结构。 它包含永远不会被覆盖的方法字节码和 a pointer to compiled code,在方法被编译之前最初为 NULL。

一个方法可以有多个入口点:

  • _i2i_entry - 指向字节码解释器的指针。
  • _code->entry_point() - JIT 编译代码的入口点。编译的方法驻留在 CodeCache - VM 动态生成代码的本机内存的特殊区域。
  • i2cc2i 适配器从解释器调用编译代码,反之亦然。需要这些适配器,因为解释方法和编译方法具有不同的调用约定(参数传递方式、框架构造方式等)

编译方法可能有不常见的陷阱,在极少数情况下会回退到解释器。此外,一个Java方法可以动态地重新编译多次,所以JVM不能丢掉原来的字节码。无论如何释放它是没有意义的,因为字节码通常比编译后的代码小得多。