提问者:小点点

OpenJdk 初始启动时间非常慢


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

-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

为什么它挂起23秒之间加载java.security.NoSuch算法异常和jdk.internal.logger.默认LoggerFinder?

编辑:根据评论,我来澄清一些。这是一台windows rdp服务器。实际上,不止是一台服务器,但问题在所有服务器上都存在。该应用程序是独立的应用程序。所以每天早上都有问题,因为登录启动应用程序的用户会在“什么都没发生”的时候多次尝试启动它。现在,我已经多次尝试重启其中一台服务器,我发现的情况如下:

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


共2个答案

匿名用户

您的应用程序可能因为缺少“类数据共享(CDS)归档”而受到影响。这样的存档允许更快地加载标准类,并且默认情况下是由以前版本的一些安装程序生成的,但是OpenJDK

这是由JEP 341解决的:

目前,JDK映像在< code>lib目录中包含一个在构建时生成的默认类列表。想要利用CDS的用户,即使只有JDK中提供的默认类列表,也必须额外运行< code>java -Xshare:dump。这个选项是有文档记录的,但是许多用户并不知道。

所以虽然这个JEP是关于JDK的

注意,通过在CDS中包含应用程序类,可以进一步缩短启动时间。另请参阅JDK的类数据共享部分

匿名用户

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

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

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

后来我就把它杀死了

splashProcess.destroy();

请注意,如果我应该使用新的JFrame()创建一个splashscreen,则通常需要35秒才能显示。

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

所以这些是我发现的结果。我希望其他人会觉得它们有用,如果有人能解释为什么我所说的“类路径索引”是这样创建的,我将是受欢迎的。