不同 java 应用程序导出选项的奇怪行为
Strange behavior with different java application export option
我有 java 服务器应用程序,它使用许多库(netty、番石榴等)。我总是将此应用程序导出为一个 .jar。当我在 Eclipse 中 运行 应用程序时,我没有遇到任何问题。但是,如果我在控制台中启动应用程序(Windows,或 Ubuntu,无关紧要),我会遇到一个奇怪的问题:所有通过套接字的连接进程持续时间太长。例如,通过 HttpAsync 或其他方式(rabbitmq 连接等)的简单 http 连接持续 1-2 分钟。但是连接完成后,数据sends/receives很快。我不知道是什么问题。前面说过,我是用Eclipse开发的
如您所知,您可以通过 3 种不同的方式导出项目(在 Eclipse 中):
- 将所需的库提取到 JAR 中。
- 将需要的库打包成 JAR。
- 将所需的库复制到 JAR 旁边的子文件夹中。
所以,当我使用 2 选项时,我遇到了问题。当我切换到 3d 选项时(主 .jar 附近文件夹中的所有 .jars),问题就解决了。
一般来说,选项 2 和选项 3 之间没有太大区别(在 2 中,所有 .jars 都在一个 jar 中)。我认为这是在执行时从罐子加载新的 类 需要额外时间的原因。但是问题不仅出现在开始时,而且出现在所有新连接上。
有人可以解释这种行为吗?
UPD: 月食月神。 OS 我正在使用什么(Windows 或 Ubuntu)无关紧要,甚至无关紧要的是什么 jvm(尝试使用不同的 Oracle jdk,甚至尝试打开jdk).
由于我们不知道您的 JAR 的确切结构,这里是一个更一般的解释(假设您 运行 您的应用程序 java -jar your_app.jar
)。
case 将所需的库复制到 JAR 旁边的子文件夹中。
- 如果 class 需要加载 class 加载程序(在 运行time JAR 之后)首先检查
your_app.jar
以找到所需的 class
- 如果 class 没有找到它遍历子文件夹中的所有 JAR 文件
- 所有 JAR 文件都可以保存在文件系统缓存中以供进一步阅读
案例将需要的库打包成JAR
- 如果 class 需要加载 Eclipse class 加载程序 JarRsrcLoader(在 运行time JAR 之后)首先检查
your_app.jar
以找到所需的 class
- 如果未找到 class,它会遍历所有嵌入的 JAR 文件,这意味着首先需要从
your_app.jar
中解压缩它们,然后才能读取内容
- 提取的嵌入式 JAR 文件不会保留在文件系统缓存中以供进一步阅读(因为它们不是文件系统中的文件)
如果你有更多的 hugh 嵌入式库 JAR,这可能会导致 class 加载速度变慢(但只有第一次 class 被 class装载机)。
如果比较
的输出,您可以看到 class 加载的差异
java -verbose:class -jar your_app_external_library_jars.jar
和
java -verbose:class -jar your_app_embedded_library_jars.jar
可以通过为每个 JAR 文件(例如 your_app.jar
和嵌入式库 JAR)生成一个 INDEX.LIST
文件来提高性能。
在第二种方法中,您在 main.jar 中拥有所有依赖项 jar。
因此,除非需要,否则它不会加载任何依赖项 jar。
然而,在第三个选项的情况下,您的 main.jar 和其他依赖项 jar 是独立的(与第二个方式不同),因此被加载以进行连接并且可用。
尝试通过操作依赖项 jar 来添加日志语句或 syso 以查看此工作。
发生这种情况是因为当您使用 "uber jar" 方法时,一些元数据可能会丢失。
这只是一个示例,但如果您下载 this and this,请查看 jar 内部。同一个 META-INF 文件夹中有几个同名文件。
这些文件可能很重要,当 eclipse 为您重新打包时,他可能没有很好地合并这些文件。
这就是你可能遇到的情况。
这都是关于打包成 JAR 时的性能差异 v/s 提取到 JAR 中以及 运行从 Eclipse v/s 宁 v/s 宁从控制台时的性能差异.
打包成 JAR 时的性能差异 v/s 提取到 JAR 中:
将所需的库提取到 JAR 中:
它的作用:
在此选项中,Eclipse 将从引用的 JAR 中提取所有 classes 并打包到生成的 JAR 中。
如果你打开 JAR,你会发现没有引用的 JAR 被打包,但是所有 class 引用的 JAR 都是按照包结构排列的,然后在根级别打包到 JAR 中。与 "Packaging required libraries into a jar file" 相比,这带来了性能上的关键差异,其中还有 运行 时间解析和在内存中加载 JAR 等的额外成本。
当通过 Eclipse 导出为 JAR 时,如果关注性能,这是最佳选择。这也是可扩展的选项,因为您可以发送此 JAR
MANIFEST.MF 此文件中要注意的主要内容是您主要 class。当您 运行 JAR 时,您直接 运行ning 您需要的 class。
Main-Class: com.my.jar.TestSSL
将需要的库打包到 JAR 中:
它的作用:
在此选项中,Eclipse 将:
- 将所有引用的 JAR 打包到生成的 JAR 中。
- 通过
org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
使用Eclipse的JAR加载机制,你也可以看到org.eclipse.jdt.internal.jarinjarloader
打包到你生成的JAR中,这个包就在生成的JAR的根目录下。
当然,这是您选择此选项时产生的额外费用,因为当您 运行 JAR 时,执行的不是主 class,而是 JarRsrcLoader
被执行,这将加载你的主 class 和其他库,并且所有引用的库都被打包。请参阅下面的 MANIFEST.MF 部分
MANIFEST.MF 此文件中要注意的主要内容是您主要 class。当您 运行 JAR 时,JarRsrcLoader
将 运行 并将做进一步的工作。
Rsrc-Main-Class: com.cgi.tmi.TestSSL
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
现在对于最后一个 Eclipse 导出选项 - "Copy required libraries into sub folder next to JAR",我不认为这是一个值得考虑的非常可扩展的解决方案,因为这会强加你的文件系统依赖性,所以我会说不要这样做。
从 Eclipse 运行ning v/s 运行ning 从控制台时的性能差异:
当您从 Eclipse 运行 应用程序时,它类似于第一个导出选项,其中 Eclipse 不需要在 运行 时间和所有时间解析和加载 JAR。
然而,这是一个非常微不足道的问题,关键是考虑 Eclipse JAR 导出选项 1 v/s 选项 2。
最后的话:
- 使用 "Extract required libraries into JAR" 导出 JAR,您将看到显着的性能提升。
- 当您从控制台 运行 时,套接字连接持续很长时间的可能性很小,因为 JVM 运行 的代码在 运行ning 时具有相同或非常可比的性能来自 Eclipse 和控制台(在这两种情况下考虑相同的 Java 版本)。您可能会因为打包的 JAR 性能而感到不适。尝试提取 JAR,你应该没问题。
- 另外,请考虑您正在执行的日志记录量。 运行通过时,根据配置,Eclipse 可能会屏蔽大量日志记录,从而为您节省 i/o 时间。
- 了解如何 classes are accessed from JAR class path,这就像从 JAR 引用 classes 时的额外计算成本。
我有 java 服务器应用程序,它使用许多库(netty、番石榴等)。我总是将此应用程序导出为一个 .jar。当我在 Eclipse 中 运行 应用程序时,我没有遇到任何问题。但是,如果我在控制台中启动应用程序(Windows,或 Ubuntu,无关紧要),我会遇到一个奇怪的问题:所有通过套接字的连接进程持续时间太长。例如,通过 HttpAsync 或其他方式(rabbitmq 连接等)的简单 http 连接持续 1-2 分钟。但是连接完成后,数据sends/receives很快。我不知道是什么问题。前面说过,我是用Eclipse开发的
如您所知,您可以通过 3 种不同的方式导出项目(在 Eclipse 中):
- 将所需的库提取到 JAR 中。
- 将需要的库打包成 JAR。
- 将所需的库复制到 JAR 旁边的子文件夹中。
所以,当我使用 2 选项时,我遇到了问题。当我切换到 3d 选项时(主 .jar 附近文件夹中的所有 .jars),问题就解决了。
一般来说,选项 2 和选项 3 之间没有太大区别(在 2 中,所有 .jars 都在一个 jar 中)。我认为这是在执行时从罐子加载新的 类 需要额外时间的原因。但是问题不仅出现在开始时,而且出现在所有新连接上。
有人可以解释这种行为吗?
UPD: 月食月神。 OS 我正在使用什么(Windows 或 Ubuntu)无关紧要,甚至无关紧要的是什么 jvm(尝试使用不同的 Oracle jdk,甚至尝试打开jdk).
由于我们不知道您的 JAR 的确切结构,这里是一个更一般的解释(假设您 运行 您的应用程序 java -jar your_app.jar
)。
case 将所需的库复制到 JAR 旁边的子文件夹中。
- 如果 class 需要加载 class 加载程序(在 运行time JAR 之后)首先检查
your_app.jar
以找到所需的 class - 如果 class 没有找到它遍历子文件夹中的所有 JAR 文件
- 所有 JAR 文件都可以保存在文件系统缓存中以供进一步阅读
案例将需要的库打包成JAR
- 如果 class 需要加载 Eclipse class 加载程序 JarRsrcLoader(在 运行time JAR 之后)首先检查
your_app.jar
以找到所需的 class - 如果未找到 class,它会遍历所有嵌入的 JAR 文件,这意味着首先需要从
your_app.jar
中解压缩它们,然后才能读取内容 - 提取的嵌入式 JAR 文件不会保留在文件系统缓存中以供进一步阅读(因为它们不是文件系统中的文件)
如果你有更多的 hugh 嵌入式库 JAR,这可能会导致 class 加载速度变慢(但只有第一次 class 被 class装载机)。
如果比较
的输出,您可以看到 class 加载的差异java -verbose:class -jar your_app_external_library_jars.jar
和
java -verbose:class -jar your_app_embedded_library_jars.jar
可以通过为每个 JAR 文件(例如 your_app.jar
和嵌入式库 JAR)生成一个 INDEX.LIST
文件来提高性能。
在第二种方法中,您在 main.jar 中拥有所有依赖项 jar。 因此,除非需要,否则它不会加载任何依赖项 jar。 然而,在第三个选项的情况下,您的 main.jar 和其他依赖项 jar 是独立的(与第二个方式不同),因此被加载以进行连接并且可用。
尝试通过操作依赖项 jar 来添加日志语句或 syso 以查看此工作。
发生这种情况是因为当您使用 "uber jar" 方法时,一些元数据可能会丢失。
这只是一个示例,但如果您下载 this and this,请查看 jar 内部。同一个 META-INF 文件夹中有几个同名文件。
这些文件可能很重要,当 eclipse 为您重新打包时,他可能没有很好地合并这些文件。
这就是你可能遇到的情况。
这都是关于打包成 JAR 时的性能差异 v/s 提取到 JAR 中以及 运行从 Eclipse v/s 宁 v/s 宁从控制台时的性能差异.
打包成 JAR 时的性能差异 v/s 提取到 JAR 中:
将所需的库提取到 JAR 中:
它的作用:
在此选项中,Eclipse 将从引用的 JAR 中提取所有 classes 并打包到生成的 JAR 中。
如果你打开 JAR,你会发现没有引用的 JAR 被打包,但是所有 class 引用的 JAR 都是按照包结构排列的,然后在根级别打包到 JAR 中。与 "Packaging required libraries into a jar file" 相比,这带来了性能上的关键差异,其中还有 运行 时间解析和在内存中加载 JAR 等的额外成本。
当通过 Eclipse 导出为 JAR 时,如果关注性能,这是最佳选择。这也是可扩展的选项,因为您可以发送此 JAR
MANIFEST.MF 此文件中要注意的主要内容是您主要 class。当您 运行 JAR 时,您直接 运行ning 您需要的 class。
Main-Class: com.my.jar.TestSSL
将需要的库打包到 JAR 中:
它的作用:
在此选项中,Eclipse 将:
- 将所有引用的 JAR 打包到生成的 JAR 中。
- 通过
org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
使用Eclipse的JAR加载机制,你也可以看到org.eclipse.jdt.internal.jarinjarloader
打包到你生成的JAR中,这个包就在生成的JAR的根目录下。
当然,这是您选择此选项时产生的额外费用,因为当您 运行 JAR 时,执行的不是主 class,而是 JarRsrcLoader
被执行,这将加载你的主 class 和其他库,并且所有引用的库都被打包。请参阅下面的 MANIFEST.MF 部分
MANIFEST.MF 此文件中要注意的主要内容是您主要 class。当您 运行 JAR 时,JarRsrcLoader
将 运行 并将做进一步的工作。
Rsrc-Main-Class: com.cgi.tmi.TestSSL
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
现在对于最后一个 Eclipse 导出选项 - "Copy required libraries into sub folder next to JAR",我不认为这是一个值得考虑的非常可扩展的解决方案,因为这会强加你的文件系统依赖性,所以我会说不要这样做。
从 Eclipse 运行ning v/s 运行ning 从控制台时的性能差异:
当您从 Eclipse 运行 应用程序时,它类似于第一个导出选项,其中 Eclipse 不需要在 运行 时间和所有时间解析和加载 JAR。
然而,这是一个非常微不足道的问题,关键是考虑 Eclipse JAR 导出选项 1 v/s 选项 2。
最后的话:
- 使用 "Extract required libraries into JAR" 导出 JAR,您将看到显着的性能提升。
- 当您从控制台 运行 时,套接字连接持续很长时间的可能性很小,因为 JVM 运行 的代码在 运行ning 时具有相同或非常可比的性能来自 Eclipse 和控制台(在这两种情况下考虑相同的 Java 版本)。您可能会因为打包的 JAR 性能而感到不适。尝试提取 JAR,你应该没问题。
- 另外,请考虑您正在执行的日志记录量。 运行通过时,根据配置,Eclipse 可能会屏蔽大量日志记录,从而为您节省 i/o 时间。
- 了解如何 classes are accessed from JAR class path,这就像从 JAR 引用 classes 时的额外计算成本。