如何加速Java虚拟机(JVM)的启动时间?

45

我正在运行一些启动多个JVM进程的测试。相比于在JVM内部运行的实际测试时间,JVM的总体启动时间相当重要。我该如何加快速度?

我已经使用了“-client”选项,这确实有所帮助,但并不如我所期望的那样。是否还有其他方法,比如预加载一堆JVM并以某种方式重用它们?

6个回答

38
如果您想重复使用JVM,则可以使用“某种方式”来使用Nailgun。 Nailgun保持一个JVM运行状态,然后使用轻量级本地客户端启动特定类并处理控制台I / O。这对于运行小型命令行Java实用程序很有用,但是由于它重复使用相同的JVM可能会积累状态。
为了解决状态累积问题,以运行多个JVM的代价,另一种选择是Drip。 Drip保留一个新的JVM备用,具有正确的类路径和其他JVM选项,因此在需要时可以快速连接和使用它,然后将其丢弃。 Drip对JVM选项进行哈希处理,并存储有关如何连接到JVM的信息在以哈希值命名的目录中。

Nailgun确实非常棒。 - Unai Vivi
@Pete Nailgun很棒,但它不安全! - Gaurav

18

JVM启动性能可以通过多种方式进行优化:CDS、直接调整、CPU绑定和jlink(自JDK 9以来)都是不错的选择。AOT(JDK 9,仅限Linux)存在一些问题,但可以进行调整以帮助小型应用程序。

CDS

类数据共享在1.5年为客户端VM开发,但自那时以来已经大大改进,可以在所有HotSpot变体上工作(自8以来所有GC),当启用时可大大提高启动性能。

CDS在32位JRE客户端安装中始终启用,但可能需要一些手动步骤才能在其他位置启用:

  1. 运行java -Xshare:dump生成CDS共享存档
  2. 在命令中添加-Xshare:auto以确保它被使用

其他调整

虽然-client可能会有所作为,也可能不会做任何事情(实际上自9以来,许多系统的JVM都没有提供客户端VM),但有许多方法可以调整HotSpot服务器JVM,使其行为更像客户端VM:

  • 仅使用C1(客户端)编译器:-XX:TieredStopAtLevel=1
  • 尽可能使用尽可能少的编译器线程:-XX:CICompilerCount=1
  • 使用单线程串行GC:-XX:+UseSerialGC
  • 限制堆使用(特别是在大型系统上),例如,-Xmx512m

这应该足以提高小型短期运行应用程序的启动速度,但可能对峰值性能产生非常负面的影响。您当然可以进一步禁用您可能不使用的功能,例如-XX:-UsePerfData(禁用某些可通过MXBeans和jvmstat检索的运行时信息)。

高级

  • jlink是Java 9中的新工具,可让您构建自定义运行时映像。如果您的控制台应用程序仅使用JDK模块的一小部分,则可以使自定义运行时变得非常小,这将进一步提高启动时间。一个最小的映像只包括java.base模块,可能会根据硬件和其他调整将启动时间提高10-20毫秒: $JAVA_HOME/bin/jlink --add-modules java.base --module-path $JAVA_HOME/jmods --output jbase

  • (仅适用于Linux) Java 9引入了实验性的AOT编译器jaotc,可用于帮助应用程序更快地引导,并极大地减少所需的循环次数。开箱即用它可能会减慢立即启动(因为AOT'd代码是共享库,增加了自己的I/O开销,不支持Serial GC..),但经过精心的调整,我们看到它可以将小型应用程序的启动时间缩短15-30%。

  • CPU固定:在大型系统上,我们看到的效果源自套接字之间的缓存一致性流量,并且绑定到单个CPU节点可以显著减少启动时间。在Linux上,像这样使用numactl --cpunodebind = 0 $JAVA_HOME/bin/java ...即可完成。

总的来说,我们已经能够让最小的应用程序在35ms(JDK 9 GA)内执行。各种启动优化已经纳入了JDK10分支,现在我看到的数字仅为28ms。


2
jaotc 已在 JDK 17 中被移除。替代方案是 GraalVM native-image,尽管它也有一些需要注意的地方。 - Aleksandr Dubinsky

2
为什么不在一个JVM中加载所有测试?你可以执行以下操作之一吗?
1. 通过更高级别的类加载所有测试? 2. 在列表(配置文件)中指定测试类,并通过Class.forName()按顺序加载每个类? 3. 如果隔离很重要,您可以为每个测试创建一个不同的类加载器。
如果内存分配相当大,则通过将JVM内存启动大小设置为可分配内存的最大值(-Xms vs -Xmx),可能会加快速度,这样将节省JVM寻找更多内存的时间。但是在这种情况下,我认为这不太可能是问题所在。

2
除了其他人说的内容,Java的下一个版本(Java 9)应该会通过平台模块化来提高JVM启动时间。根据这个链接,模块化的好处之一是使平台下载更小,可能提高启动性能。更小的内存占用也可以显著提高性能,特别是对于桌面应用程序。请注意保留HTML标签。

5
显然,这已经被推迟到JDK 8了。(来源:http://openjdk.java.net/projects/jdk7/features/) - Calvin
2
我真的希望项目Coin或者他们用来模块化核心库的任何东西不要总是被推迟。我很想看到通过放弃Enumeration和其他早期错误的开始来减少核心库代码。 - Edwin Buck
1
经过“一些”时间,Java 9 终于要来了 :D - thatsIch
OpenJ9应该具有类似的功能。 - Thorbjørn Ravn Andersen

1

使用Java 5或更高版本运行测试。对于Java 5,启动时间已经显著缩短。

除非您可以重用正在运行的VM,否则无法加快速度:预先启动大量VM,然后在每个VM中运行单个测试不会使测试变得更快。

因此,您需要想出一种方法在单个VM中运行多个测试。


0

如果你真的需要为每个测试启动一个单独的虚拟机,那么你最好使用Sun发布的最新VM版本之一。随着Java版本的更新,启动时间已经大大缩短,在1.6.0_16版本中,“Hello world”程序在我的系统上运行大约需要0.2秒。

如果你的问题更多地是“如何在一个VM中运行更多的测试”,最简单的方法取决于你使用的测试框架(如果你没有使用框架,你应该考虑使用)。使用JUnit和可能是JUnit的ant目标,你可以使用模式匹配要运行的测试,或者将不同的测试类加入到一个测试套件中,然后在一个VM中运行。


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