OpenJdk的初始启动时间非常慢

问题描述 投票:4回答:2

我在服务器上运行openjdk 11.0.3。每当服务器重新启动时(每晚):对于我的应用程序的第一次初始启动,用户必须在应用程序启动之前等待35秒。 (在从main方法编写第一个System.out.println之前。)(后续启动非常快)我尝试了以下选项来调试它:

-Xlog:class+load:file=classload.txt

以下是最重要的发现:

... 
[2.284s][info][class,load] jdk.internal.loader.URLClassPath$FileLoader source: jrt:/java.base
[5.032s][info][class,load] sun.security.rsa.RSASignature$SHA1withRSA source: jrt:/java.base
…
[5.051s][info][class,load] java.util.LinkedList$Node source: jrt:/java.base 
[8.121s][info][class,load] pos.LFChangeable source: file:/C:/Users/rho/AppData/Roaming/edapp/pos.jar
…
[8.135s][info][class,load] java.io.FileNotFoundException source: jrt:/java.base
[10.584s][info][class,load] sun.reflect.misc.ReflectUtil source: jrt:/java.base
…
[11.744s][info][class,load] java.security.NoSuchAlgorithmException source: jrt:/java.base
[34.853s][info][class,load] jdk.internal.logger.DefaultLoggerFinder source: jrt:/java.base

为什么在加载java.security.NoSuchAlgorithmException和jdk.internal.logger.DefaultLoggerFinder之间挂起23秒?那么减速的其他几秒呢?

编辑:根据评论,我会澄清一些。这是一个Windows rdp服务器。实际上,它不止一台服务器,但问题仍然存在于所有服务器上。该应用程序是一个独立的应用程序。所以每天早上都有问题,因为登录启动应用程序的用户会在“没有任何反应”的情况下尝试多次启动它。我现在尝试重启其中一个服务器很多次了,这就是我发现的:

在重新启动后使用java11启动我的应用程序平均在第一个System.out.println之前40秒。然后在我的第一个JFrame显示之前只有1-2秒。重新启动后使用java8(sun)启动我的应用程序在第一个System.out.println之前平均需要16秒。但是在我的第一个JFrame显示之前,我得到了25秒的延迟。在使用java8启动后,使用java11启动我的应用程序平均需要4-6秒。

java classloader classloading openjdk-11
2个回答
2
投票

您的应用程序可能缺少“类数据共享(CDS)存档”。这样的存档允许更快地加载标准类,并且默认情况下由某些先前版本的安装程序生成,但OpenJDK 11没有安装程序。

这是由JEP 341解决的:

目前,JDK映像包括在构建时在lib目录中生成的默认类列表。想要利用CDS的用户,即使只使用JDK中提供的默认类列表,也必须运行java -Xshare:dump作为额外步骤。此选项已记录在案,但许多用户并未意识到这一点。

因此,虽然这个JEP是关于JDK 12自动执行必要的步骤,但它也提到了JDK 11的修复:只需在命令行上运行一次java -Xshare:dump,就可以生成存档。

请注意,通过在CDS中包含应用程序类,您可以进一步缩短启动时间。另请参阅JDK 11文档的Class Data Sharing部分。


0
投票

我现在已经进行了广泛的测试,我准备发布我的结果,以及我制作的两个不同的“解决方案”。首先,让我解释一下我的应用程序。它是一个摇摆的企业应用程序,它在13年前开始了它的生命,并且从那以后一直延续。因此,这个应用程序很大,做了很多不同的事情(虽然大多数用户只使用它的一部分),并且在它的类路径上包含大约120个jar文件,包括所有第三方jar。如前所述,重新启动服务器后,我需要35秒才能显示第一次登录-JFrame。

解决方案1:这是我的第一个解决方案,并不是慢启动的解决方案,而是更多用户无法启动应用程序的多个实例的解决方案。我注意到虽然我的应用程序在第一次初始启动时非常慢,但其他应用程序却没有。因此,解决方法是制作一个小型独立应用程序来显示启动画面,我在我的程序中这样开始:

splashProcess = Runtime.getRuntime().exec("javaw -jar splash.jar");

后来我就把它杀掉了

splashProcess.destroy();

请注意,如果我应该使用新的JFrame()来创建一个启动画面,它通常需要35秒才会显示。

解决方案2:在测试时,我发现我可以通过删除所有jar文件并将其复制回来来模拟重启。除了减少测试时间之外,我发现只使用初始启动所需的4-5个jar文件启动应用程序非常快(虽然这会导致后面的ClassNotFoundExceptions),这也是我可以尝试的找出导致挂起的jar文件,首先复制所有jar文件,然后再省略一个。但是,我发现这不是一个责备的jar文件。每次删除一些jar文件时,应用程序启动之前所需的秒数会逐渐减少一点。所以,似乎问题是我第一次在我的应用程序中调用新的JFrame()时,java似乎在类路径中构建了某种索引或所有类的东西,尽管它们目前还没有被使用。我不知道为什么会这样做,但这个过程需要相当长的时间在类路径上有120个jar文件。这导致我解决方案nr 2.当我的应用程序现在开始时,我检查参数“startSilent”。如果存在,我的应用程序唯一做的就是显示一个大小为0,0的新JDialog,然后调用System.exit(0);然后,我创建了一个脚本,使用“startSilent”参数运行我的应用程序,该参数在用户登录时启动。现在,如果用户登录服务器并在启动应用程序之前等待至少35秒,那么现在开始闪电般快速,因为应用程序已经启动并退出一次,因此构建了“classpath-index”或其他任何内容。如果用户在较短时间后启动应用程序,则启动时间将减少静默脚本已运行的时间。 (并且在桌面准备好之前脚本启动时,开始将至少比以前更快一些程度)。

所以这些是我的研究结果。我希望其他人会发现它们很有用,如果有人能解释为什么我称之为“classpath-index”就是这样,我会受到欢迎。

© www.soinside.com 2019 - 2024. All rights reserved.