为什么 JVM 不缓存 JIT 编译的代码?

Sun 的规范 JVM 实现对字节码应用了一些相当复杂的优化,以便在代码运行几次之后获得接近本机的执行速度。

问题是,为什么这些编译后的代码没有缓存到磁盘中,以便在后续使用相同的函数/类时使用?

按照目前的情况,每次执行程序时,JIT 编译器都会重新启动,而不是使用预编译版本的代码。当字节码被解释时,添加这个特性不会显著提高程序的初始运行时间吗?

11643 次浏览

Oracle 的 JVM 确实是这样记录的——引用 Oracle,

编译器可以利用 Oracle JVM's class resolution model to 可选持久化编译的 Java 跨数据库调用的方法, 会话或实例 persistence avoids the overhead of 不必要的重新编译 会话或实例 我们知道语义上的 Java 代码 没有改变。

我不知道为什么所有复杂的 VM 实现都不提供类似的选项。

Without resorting to cut'n'paste of the link that @MYYN posted, I suspect this is because the optimisations that the JVM performs are not static, but rather dynamic, based on the data patterns as well as code patterns. It's likely that these data patterns will change during the application's lifetime, rendering the cached optimisations less than optimal.

因此,您需要一种机制来确定 than 保存的优化是否仍然是最优的,在这一点上,您可能只是在运行中重新优化。

我不知道实际的原因,没有以任何方式参与到 JVM 实现中,但是我可以想到一些似是而非的原因:

  • Java 的想法是成为一种一次编写就可以在任何地方运行的语言,把预编译的内容放入类文件有点违反了这一点(只是“有点”,因为当然实际的字节代码仍然存在)
  • 它会增加类文件的大小,因为这里会有多次相同的代码,特别是当你碰巧在多个不同的 JVM 下运行相同的程序时(当你认为不同的版本是不同的 JVM 时,这种情况并不少见,这是你必须要做的)
  • 类文件本身可能不是可写的(尽管检查起来很容易)
  • JVM 优化部分基于运行时信息,而在其他运行中,它们可能不那么适用(尽管它们仍然应该提供一些好处)

但我真的只是猜测,你也看到了,我不认为我的任何理由是真正的阻碍。我认为 Sun 没有把这种支持作为优先考虑的事情,也许我的第一个理由是接近事实,因为这样做可能会让人们习惯性地认为 Java 类文件确实需要为每个虚拟机提供一个单独的版本,而不是跨平台的。

我的首选方法实际上是有一个单独的字节码到本机的转换器,您可以事先使用它来显式地执行类似的操作,创建为特定 VM 显式构建的类文件,其中可能包含原始字节码,这样您也可以使用不同的 VM 运行。但这可能来自于我的经验: 我大部分时间都在做 JavaME,Java 编译器在编译方面不够聪明,这真的很伤人。

Excelsior JET 自2001年发布的2.0版本以来就有一个缓存 JIT 编译器。此外,它的 AOT 编译器可以使用所有优化将缓存重新编译为单个 DLL/共享对象。

对现有答案的更新—— Java8有一个专门解决这个问题的 JEP:

= > 一个 href = “ http://openjdk.java.net/JEP/145”rel = “ noReferrer”> JEP 145: 缓存编译代码 . 新链接.

在非常高的水平上,其声明的 目标是:

Save and reuse compiled native code from previous runs in order to 提高大型 Java 应用程序的启动时间。

希望这个能帮上忙。