【问题标题】:Why doesn't the JVM cache JIT compiled code?为什么 JVM 不缓存 JIT 编译的代码?
【发布时间】:2010-12-31 20:08:06
【问题描述】:

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

问题是,为什么不将此编译后的代码缓存到磁盘以供后续使用同一函数/类时使用?

就目前而言,每次执行程序时,JIT 编译器都会重新启动,而不是使用代码的预编译版本。当字节码本质上是被解释时,添加这个特性不会显着增加程序的初始运行时间吗?

【问题讨论】:

  • 一个讨论这个问题的线程:javalobby.org/forums/thread.jspa?threadID=15812
  • 但一个不太可能吸引明确答案的问题。
  • 我不确定“显着”的提升,因为那样你就必须从磁盘加载 JITted 的东西,而不是 JIT 在内存中。它可以加快速度,但要视具体情况而定。
  • 感谢大家的精彩回答!所有的答案都同样有效,所以我和社区一起去了这个......
  • @Nfff3 看看this answer

标签: java caching jvm compilation jit


【解决方案1】:

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

=> JEP 145: Cache Compiled CodeNew link.

在非常高的层次上,其声明的目标是

保存并重用以前运行的已编译本机代码,以便 提高大型 Java 应用程序的启动时间。

希望这会有所帮助。

【讨论】:

  • 该功能尚未到达the final release
  • @assylias 和 AOT,这可能永远不需要
【解决方案2】:

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

【讨论】:

  • 是的,但问题是关于规范的 JVM,即 Sun 的 JVM。我很清楚有几个用于 Java 的 AOT 编译器以及其他缓存 JVM。
【解决方案3】:

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

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

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

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

【讨论】:

  • 类文件中有这样的地方,事实上这是最初的意图(将 JIT 代码作为属性存储在类文件中)。
  • @TofuBeer:感谢您的确认。我怀疑可能是这种情况(这就是我会做的),但不确定。编辑以将其作为可能的原因删除。
  • 我认为你的最后一个要点一针见血。其他的可以解决,但我认为最后一部分是 JITed 代码没有被持久化的主要原因。
  • 关于显式字节码到本机编译器的最后一段是您目前在 .NET 中使用 NGEN (msdn.microsoft.com/en-us/library/6t9t5wcf(VS.71).aspx) 所拥有的。
【解决方案4】:

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

因此,您需要一种机制来确定保存的优化是否仍然是最佳的,此时您不妨立即重新优化。

【讨论】:

  • ...或者您可以将持久性作为一个选项提供,就像 Oracle 的 JVM 所做的那样——授权高级程序员在以下情况下优化他们的应用程序的性能他们只是知道模式在他们的责任下没有改变。为什么不呢?!
  • 因为它可能不值得。如果 SUN、IBM 和 BEA 都认为其性能 JVM 不值得,那将是有充分理由的。也许他们的 RT 优化比 Oracle 的快,这就是 Oracle 缓存它的原因。
  • 为什么不将存储的优化作为起点,使用之前运行中学到的知识?从那里 JIT 可以像往常一样重新优化东西。在关闭时,该代码可以再次持久化并在下一次运行中用作新的起点。
  • @Puce 我能想到的唯一原因是 AFAIK 你没有从运行优化代码中获得分析统计信息。所以你没有办法改进......
  • 我个人会很好地使用“在运行之间保留 JIT 分析信息”选项以及所有警告“这仅对完全相同的 JVM、相同的数据等有效,否则将被忽略”。关于为什么没有实现这一点,我预计持久化和验证 JIT 种子数据的复杂性太大,无法从其他项目中获取资源。鉴于此和 Java 8 lambda+streams 之间的选择,我宁愿选择后者。
【解决方案5】:

Oracle's JVM 确实被记录在案——引用 Oracle,

编译器可以利用 Oracle JVM 的类解析模型 可选择持久化编译的 Java 跨数据库调用的方法, 会话或实例。这样的 持久性避免了开销 不必要的重新编译 会话或实例,当它是 已知语义上的 Java 代码 没有改变。

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

【讨论】:

  • 因为其他复杂的 JVM 没有一个很好的企业 RDBMS 来方便地存储东西:)
  • 哇!这意味着编译有时会被缓存。这是个好消息!
  • IBM 的 J9 也被记录在案。
  • 请注意,这个 Oracle JVM 是 Oracle 数据库中的那个,而不是 Oracle 在购买 Sun 时获得的下载。
猜你喜欢
  • 2011-03-18
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
  • 2010-12-02
  • 2016-05-24
  • 2016-11-23
相关资源
最近更新 更多