Java 是否保存了其运行时优化?

Does Java save its runtime optimizations?

提问人:Matt Gregory 提问时间:9/16/2008 最后编辑:Basit AnwerMatt Gregory 更新时间:12/20/2018 访问量:2684

问:

我的教授对一个小程序做了一个非正式的基准测试,Java 时间是:第一次运行 1.7 秒,之后运行 0.8 秒。

  • 这完全是由于将运行时环境加载到操作环境中吗?

  • 它是否受到 Java 优化代码和存储这些优化结果的影响(对不起,我不知道这方面的技术术语)?

Java 优化 缓存

评论


答:

5赞 Curt Hagenlocher 9/16/2008 #1

据我所知,没有任何广泛使用的虚拟机可以在程序调用之间保存统计使用数据——但这无疑是未来研究的一个有趣的可能性。

您所看到的几乎可以肯定是由于磁盘缓存造成的。

评论

0赞 lscoughlin 7/27/2018
这实际上正是 Azul 的 zing jvm 的特性之一。
0赞 Cem Catikkas 9/16/2008 #2

Java JVM(实际上可能会从 JVM 的不同实现中更改)在刚开始时将解释字节码。一旦它检测到代码将运行足够多的次数,就会将其 JIT 转换为本机机器语言,以便它运行得更快。

评论

1赞 Allain Lalonde 9/16/2008
我认为他的意思是从JVM的一次运行到另一次运行。我认为这不适用。
6赞 Matt Gregory 9/16/2008 #3

好的,我找到了我读到的地方。这些都是来自“学习 Java”(O'Reilly 2005):

传统 JIT 编译的问题在于优化代码需要时间。因此,JIT 编译器可以产生不错的结果,但在应用程序启动时可能会遭受显着的延迟。对于长时间运行的服务器端应用程序来说,这通常不是问题,但对于在功能有限的较小设备上运行的客户端软件和应用程序来说,这是一个严重的问题。为了解决这个问题,Sun的编译器技术HotSpot使用了一种称为自适应编译的技巧。如果你看看程序实际上花了什么时间在做什么,就会发现它们几乎把所有的时间都花在了一次又一次地执行相对较小的代码部分上。重复执行的代码块可能只是整个程序的一小部分,但其行为决定了程序的整体性能。自适应编译还允许 Java 运行时利用在静态编译语言中根本无法完成的新型优化,因此声称 Java 代码在某些情况下可以比 C/C++ 运行得更快。

为了利用这一事实,HotSpot 最初是一个普通的 Java 字节码解释器,但有一个区别:它在执行时测量(分析)代码,以查看哪些部分正在重复执行。一旦知道代码的哪些部分对性能至关重要,HotSpot 就会将这些部分编译为最佳的本机机器代码。由于它只将程序的一小部分编译为机器代码,因此它可以花费必要的时间来优化这些部分。程序的其余部分可能根本不需要编译,只需解释即可,从而节省内存和时间。事实上,Sun 默认的 Java VM 可以在以下两种模式下运行:客户端和服务器,这告诉它是强调快速启动时间和内存节省,还是强调性能。

在这一点上,一个自然而然的问题是,为什么要在每次应用程序关闭时都丢弃所有这些良好的分析信息?在Java 5.0的发布中,Sun通过使用共享的只读类,以优化的形式持久存储,部分地提出了这个话题。这大大减少了在给定机器上运行许多 Java 应用程序的启动时间和开销。这样做的技术很复杂,但想法很简单:优化程序中需要快速运行的部分,而不用担心其余部分。

我有点想知道 Sun 自 Java 5.0 以来取得了多大的进展。

2赞 big_peanut_horse 9/16/2008 #4

我同意这可能是磁盘缓存的结果。

仅供参考,IBM Java 6 VM 确实包含一个预先编译器 (AOT)。代码并不像 JIT 生成的代码那样优化,但它存储在 VM 中,我相信某种持久的共享内存。它的主要好处是提高启动性能。默认情况下,IBM VM 在被调用 1000 次后对方法进行 JIT。如果它知道一个方法将在 VM 启动期间被调用 1000 次(想想一个常用的方法,比如 ),那么它将其存储在 AOT 缓存中是有益的,这样它就不必浪费时间在运行时编译。java.lang.String.equals(...)

1赞 Kevin Day 9/16/2008 #5

我同意海报看到的性能差异很可能是由于将 JRE 带入内存的磁盘延迟引起的。实时编译器 (JIT) 不会对小型应用程序的性能产生影响。

Java 1.6u10 (http://download.java.net/jdk6/) 在后台进程中接触运行时 JAR(即使 Java 未运行),以将数据保留在磁盘缓存中。这大大缩短了启动时间(这对桌面应用程序来说是一个巨大的好处,但对服务器端应用程序来说可能价值不大)。

在大型、长时间运行的应用程序中,JIT 会随着时间的推移产生很大的影响 - 但与应用程序的整体生命周期(大多数运行数月)相比,JIT 积累足够的统计信息来启动和优化所需的时间(5-10 秒)非常非常短。虽然存储和恢复 JIT 结果是一项有趣的学术练习,但实际改进并不是很大(这就是为什么 JIT 团队更专注于 GC 策略之类的事情,以最大限度地减少内存缓存未命中等)。

运行时类的预编译确实对桌面应用程序有很大帮助(前面提到的 6u10 磁盘缓存预加载也是如此)。

1赞 eckes 9/17/2008 #6

您应该描述一下 Benchmark 是如何完成的。尤其是在你开始测量时间的时候。

如果包括 JVM 启动时间(这对于对用户体验进行基准测试很有用,但对优化 Java 代码没有太大用处),那么它可能是文件系统缓存效应,也可能是由称为“Java 类数据共享”的功能引起的:

对于太阳:

http://java.sun.com/j2se/1.5.0/docs/guide/vm/class-data-sharing.html

在这个选项中,JVM 将运行时类的准备好的映像保存到文件中,以便在下次启动时更快地加载(和共享)这些类。您可以使用 Sun JVM 的 -Xshare:on 或 -Xshare:off 来控制这一点。默认值为 -Xshare:auto,如果存在,它将加载共享类映像,如果不存在,如果目录可写,它将在首次启动时写入它。

顺便说一句,在 IBM Java 5 中,这更加强大:

http://www.ibm.com/developerworks/java/library/j-ibmjava4/

我不知道有任何主流JVM正在保存JIT统计信息。