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

120

Sun 公司提供的标准 JVM 实现会对字节码进行高级优化,使得代码在多次运行后能够获得接近原生执行速度。

问题是,为什么不将这些编译后的代码缓存到磁盘上,在后续使用相同函数或类时进行使用呢?

目前情况是,每次程序运行时,JIT 编译器都需要重新启动,而不是使用预编译版本的代码。如果增加这个功能,那么在程序刚开始运行时,即在字节码被解释执行时,将会显著提升程序的初始运行时间。


4
讨论这个问题的帖子:http://www.javalobby.org/forums/thread.jspa?threadID=15812 - miku
2
但这是一个不太可能吸引到明确答案的问题。 - bmargulies
1
我对于是否会有一个“显著”的提升并不确定,因为这样的话你将不得不从磁盘加载JIT编译后的内容,而不是在内存中进行JIT编译。这可能会加快速度,但是需要根据具体情况来看。 - R. Martinho Fernandes
1
谢谢大家提供的出色答案!所有的答案都是同样有效的,所以我选择了社区的意见... - Chinmay Kanchi
1
@Nfff3 请看一下这个答案 - apangin
显示剩余2条评论
5个回答

33

不需使用@MYYN发帖中的链接,我怀疑这是因为JVM执行的优化不是静态的,而是动态的,基于数据模式和代码模式。很可能这些数据模式会在应用程序生命周期内改变,从而使缓存的优化不够优化。

所以你需要一种机制来确定这些保存的优化是否仍然是最佳的,在这种情况下,你最好还是动态重新进行优化。


7
或者你可以像Oracle的JVM一样,将持久性作为一种选项提供--使高级程序员能够在他们知道模式不发生变化且在他们的责任范围内的地方优化应用程序的性能。为什么不呢?! - Alex Martelli
2
因为这可能不值得。如果SUN、IBM或BEA都认为它不值得为他们的性能JVM做出努力,那么一定有很好的理由。也许他们的RT优化比Oracle更快,这就是为什么Oracle会缓存它的原因。 - skaffman
12
为什么不以存储的优化结果为起点,利用之前的运行所学到的东西呢?然后 JIT 可以像平常一样工作并重新优化代码。在关闭时,该代码可以再次被持久化,作为下一次运行的新起点。 - Puce
2
我个人认为,“只需在运行之间保留JIT分析信息”选项是可以接受的,但需要提醒用户“只有在完全相同的JVM、相同的数据等情况下才有效,否则将被忽略”。至于为什么没有实现这个选项,我认为这会增加持久化和验证JIT种子数据的复杂性,从而占用其他项目的资源。如果选择在Java 8 lambda+streams之间做出选择,我宁愿选择后者。 - Thorbjørn Ravn Andersen
1
@KorayTugay...如果在共享文件系统上进行操作,情况可能会变得非常令人兴奋。我可以想象整个过程会成为一场噩梦。 - SusanW
显示剩余5条评论

32

Oracle 的 JVM 的确有文档可以实现这个功能 -- 引用 Oracle 的说法,编译器可以利用 Oracle JVM 的类解析模型选择性地在数据库调用、会话或实例之间保持已编译的 Java 方法。这样的保持可以避免在已知语义上 Java 代码未更改时跨会话或实例不必要的重新编译开销。

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


8
因为其他复杂的JVM没有一个巨大的企业级RDBMS可用来存储东西 :) - skaffman
哇!这意味着编译有时会被缓存。这是个好消息! - Sandeep Jindal
IBM的J9也有记录表明可以这样做。 - user314104
11
请注意,这个 Oracle JVM 是嵌入在 Oracle 数据库中的,不是购买 Sun 公司时下载的那个。 - Thorbjørn Ravn Andersen

20

针对现有答案的更新 - Java 8有一个专门解决这个问题的JEP:

=> JEP 145: 缓存已编译代码新链接

在非常高的层面上,它所述的目标是:

保存和重用先前运行中生成的本机代码,以改进大型Java应用程序的启动时间。

希望这可以帮助到您。


4
该功能尚未被包含在最终版本中。 - assylias
@assylias,有了“AOT”,这可能永远不会需要。 - Eugene
JEP 145(由于编译代码的重用而实现更快的JVM启动)发生了什么情况? - Basil Bourque

8
自2001年发布2.0版本以来,Excelsior JET具备缓存JIT编译器。此外,它的AOT编译器可以将缓存重新编译为单个DLL/shared object,并使用所有优化。

4
是的,但问题是关于规范的JVM,也就是Sun的JVM。我很清楚Java有几个AOT编译器以及其他缓存JVM。 - Chinmay Kanchi

1

我不知道实际原因,也没有参与JVM实现,但我可以想到一些合理的原因:

  • Java的理念是成为一种“编写一次,随处运行”的语言,将预编译的内容放入类文件中有点违反这个理念(只是“有点”是因为当然实际的字节码仍然存在)
  • 这会增加类文件的大小,因为你会在其中多次重复相同的代码,特别是如果你在多个不同的JVM下运行同一个程序(这并不罕见,因为你必须将不同的版本视为不同的JVM)
  • 类文件本身可能无法写入(虽然检查这一点非常容易)
  • JVM优化部分基于运行时信息和其他运行情况,它们可能不太适用于其他运行情况(尽管它们仍然应该提供一些好处)

但我真的只是猜测,而且正如你所看到的,我认为我的任何理由都不是真正的难题。我认为Sun只是不认为这种支持是一个优先事项,也许我的第一个原因接近真相,因为习惯性地这样做可能会让人们认为Java类文件确实需要为每个VM单独版本,而不是跨平台。

我的首选方式实际上是拥有一个独立的字节码到本地代码的转换器,你可以使用它来显式地预处理某些内容,创建专为特定虚拟机构建的类文件,可能还包含原始字节码,这样你就可以在不同的虚拟机上运行。但这可能来自于我的经验:我一直在做 Java ME,Java 编译器在编译方面并不聪明,这让我很受伤。


1
在类文件中有一个位置可以存储这些内容,事实上这也是最初的意图(将 JIT 编译后的代码作为类文件的属性存储)。 - TofuBeer
@TofuBeer:感谢确认。我怀疑可能是这种情况(这就是我会做的),但不确定。编辑以排除这种可能性。 - JaakkoK
我认为你在最后一个要点上说得很对。其他的可以绕过去,但我认为最后一个部分是JIT代码不持久化的主要原因。 - Sasha Chedygov
1
关于显式的字节码到本地编译器的最后一段是你目前在.NET中使用NGEN(http://msdn.microsoft.com/en-us/library/6t9t5wcf(VS.71).aspx)。 - R. Martinho Fernandes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接