为什么JVM启动缓慢?

66

相对于其他运行时环境(例如CPython),JVM(特别是Sun的实现)为什么启动速度较慢?我的印象是这主要与大量库被加载而不管它们是否需要有关,但这似乎不应该需要10年来解决。

话说回来,JVM的启动时间与Windows上的CLR比较如何?Mono的CLR呢?

更新:我特别关心在Unix中常见的小型工具链使用情况。Java现在是否适合这种风格?无论Java的启动开销如何,它是为每个Java进程都加上吗,还是仅在第一个进程中表现出开销?


1
使用主观标记。"慢"是一个主观的描述。 - Martin OConnor
28
“慢”是主观的,但这个问题不是。 - Zifre
1
据我所知,谷歌为Node.js开发的V8 JavaScript引擎大约需要30毫秒。我怀疑这比JVM慢不了多少。看起来JVM需要大约70毫秒。http://tinyurl.com/phkwn45 - Alexander Mills
1
这是一个非常好的问题,但回答却很糟糕。 - Nolan
8个回答

23

这里是维基百科关于该问题的说法(含一些参考资料)。

看起来大部分时间都花在从磁盘加载数据(类)上了(即启动时间受 I/O 限制)。


3
一个澄清:不仅仅是磁盘I/O,而更多的是从JAR(WAR,EAR)中查找类,这些都是压缩的ZIP归档文件。提取需要CPU,特别是如果它们是压缩的。 - StaxMan
5
CPU解压缩时间极短。实际上,StackOverflow本身使用压缩作为性能优化,而压缩比解压缩要昂贵得多。 - Tom Hawtin - tackline
2
@Tom:解压一个2kb的类文件所需的CPU时间很少。解压30mb的类文件以读取一个2kb的类文件所需的CPU时间则相当显著。 - Seun Osewa
6
一个jar文件使用zip压缩,如果只需要读取一个文件,无需解压整个存档文件,每个条目(文件)都是独立压缩的,与其他条目无关。 - josefx
2
Zip压缩文件以中心目录条目结束,其中列出了条目名称及其偏移量。您可以跳转到结尾,查看目录,然后跳转到所需条目。 - josefx
显示剩余5条评论

8

请注意以下解决方案:

JVM有两种机制可以加快启动速度。 第一种是类数据共享机制,自Java 6 Update 21以来已得到支持(只适用于HotSpot客户端VM,并且据我所知仅适用于串行垃圾收集器)

要激活它,您需要设置 -Xshare (在某些实现中: -Xshareclasses )JVM选项。

要了解更多信息,请访问: 类数据共享

第二种机制是Java快速启动程序。 它允许在操作系统启动期间预加载类,请参见: Java Quick Starter 以获取更多详细信息。


Java快速启动链接已失效 :/ - Ludwik

5
在我的机器上,使用1.6(Java 6)客户端JVM运行简单的Java应用似乎是瞬间完成的。Sun公司尝试对客户端JVM进行调优以实现更快的启动速度(且客户端JVM是默认选项),因此如果您不需要大量额外的jar文件,则启动应该很快。

3
无论是什么情况,jvm仍然是启动最慢的虚拟机。在某些等级的计算机上和某些大小的应用程序上,您可能会发现它已经足够快了。但是,它的启动时间仍然可以毫无争议地被认为是相对较慢的。 - Sake
我认为GUI应用程序是导致Java被认为很慢的主要原因...必须加载大量的AWT和Swing类可能会使其至少需要一秒钟的时间。 - Andy

4
如果您正在使用Sun的HotSpot x86_64(64位编译版本),请注意当前实现仅适用于服务器模式,即它会使用完全优化来预编译加载的每个类,而32位版本还支持客户端模式,通常推迟优化,并仅优化最耗费CPU的部分,但启动时间更快。
例如,请参见:

话虽如此,至少在我的机器上(Linux x86_64 with 64bit kernel),32位HotSpot版本支持客户端和服务器模式(通过-client和-server标志),但默认为服务器模式,而64位版本仅支持服务器模式。


你确定你的信息是正确的吗?我还没有听说过现代Sun JVM会为所有类进行完整的JIT编译。而且在大多数系统上,服务器模式绝对不会这样做。客户端模式和服务器模式之间的区别更多地涉及不同的配置设置,例如用于JIT内联的阈值。 - StaxMan
服务器 HotSpot 在 10,000 次迭代之前不进行编译。客户端在 1,500 次迭代时进行编译。然而,服务器的实现方式略有不同(使用两个中间表示,如果我没记错的话)。 - Tom Hawtin - tackline

3

这取决于启动时你正在做什么。如果你运行Hello World应用程序,在我的机器上只需要0.15秒。

然而,Java更适合作为客户端或服务器/服务运行,这意味着启动时间不像连接时间(约0.025毫秒)或往返响应时间(<< 0.001毫秒)那样重要。


2
@AlexMills 值得注意的是,这些研究人员使用的是一台2006年的机器。双核i3-4370可能会快4-5倍。尽管如此,他们的分析很可能是最新的。 - Peter Lawrey
可能,但我认为只有一个核心负责加载JVM。我敢打赌SSD和HDD之间会有更大的差别。 - Alexander Mills
1
如果他们使用清空缓存的方式进行测试,那么会有所不同。我怀疑他们是在内存中运行基准测试,但很难确定,因为他们只有1 GB 的内存。 - Peter Lawrey

1

有很多原因:

  • 需要加载大量的jar文件
  • 验证(确保代码不会做坏事)
  • JIT(即时编译)开销

我不确定CLR是否也是这样,但我认为它通常更快,因为它会缓存程序集的本地版本以供下次使用(因此不需要JIT)。CPython启动更快,因为它是一个解释器,并且我IRC,不进行JIT。


使用 -target 1.6 编译的类在验证时速度更快(Harmony 验证器也更快)。 - Tom Hawtin - tackline
1
CPython 在执行 Python 文件之前会将其编译成字节码,就像 Java 一样。 - Bastien Léonard
@Bastien:字节码和机器码之间有很大的区别。编写虚拟机字节码比生成机器码容易得多。 - Zifre

1
除了已经提到的事情(加载类,特别是从压缩的JAR文件中加载);在HotSpot编译常用字节码之前以解释模式运行;以及HotSpot编译开销,JDK类本身也有相当多的一次性初始化工作。
许多优化都是为长时间运行的系统而做的,启动速度不是那么重要。
至于Unix风格的管道:您肯定不希望多次启动和重新启动JVM。那样不会很有效率。而是应该在JVM内部链接工具。这不能轻松地与非Java Unix工具混合使用,除非从JVM内部启动这些工具。

3
可以使用 Nailgun 这样的工具来解决这个问题!(已经晚了三年) - salezica

-1

所有具有丰富类型系统(例如Java或CLR)的虚拟机与那些在C或C++中发现的不太丰富的系统相比,都不会瞬间实例化。这主要是因为虚拟机中发生了很多事情,许多类被初始化并被运行系统所需。初始化系统的快照确实有所帮助,但将该图像重新加载到内存中仍然需要成本等。

一个简单的hello world样式的一行类与主要仍需要加载和初始化很多。验证类需要进行大量依赖性检查和验证,所有这些都需要执行时间和许多CPU指令。另一方面,C程序不会执行任何此类操作,仅需要一些指令,然后调用打印机函数即可。


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