繁体   English   中英

使用不同的java应用程序导出选项的奇怪行为

[英]Strange behavior with different java application export option

我有java服务器应用程序,它使用很多库(netty,guava等)。 我总是将此应用程序导出为单个.jar。 当我在Eclipse中运行应用程序时,我没有任何问题。 但是,如果我在控制台(Windows或Ubuntu,无关紧要)启动应用程序,我有一个奇怪的问题:通过套接字的所有连接进程持续太长时间。 例如,通过HttpAsync或其他人(rabbitmq连接等)的简单http连接持续1-2分钟。 但连接完成后,数据发送/接收速度很快。 我无法弄清楚是什么问题。 如前所述,我使用Eclipse进行开发。

如您所知,您可以导出项目3方式(在Eclipse中):

  1. 将所需的库提取到JAR中。
  2. 将所需的库打包到JAR中。
  3. 将所需的库复制到JAR旁边的子文件夹中。

所以,当我使用2选项时,我遇到了问题。 当我切换到3d选项(main .jar附近的文件夹中的所有.jars)时,问题就解决了。

通常情况下,2和3选项之间没有太大区别(2个所有.jars只在一个jar内)。 我认为这是从jar开始在执行时加载新类所需的额外时间的原因。 但问题不仅发生在开始,而且发生在所有新连接上。

有人可以解释这种行为吗?

UPD: Eclipse Luna。 无论我使用什么操作系统(Windows,或Ubuntu),甚至无关紧要jvm(尝试使用不同的Oracle jdk,甚至尝试打开jdk)。

这一切都讨论了在从JAR v / s解压缩到JAR时的性能差异以及从从控制台运行的Eclipse v / s运行时的性能差异。

打包到JAR中的JAR v / s中的性能差异:

将所需的库提取到JAR中:

它能做什么:
在此选项中,Eclipse将从引用的JAR和包中提取所有类到生成的JAR中。

如果打开JAR,那么您会发现没有打包引用的JAR,但所有引用的JAR类都按照包结构排列,然后在根级别打包在JAR中。 “将所需包装到jar文件中”相比,这带来了性能上的关键差异,其中还存在运行时解析和在内存中加载JAR等成本。

当通过Eclipse导出为JAR时,如果关注性能,则最好选择。 这也是可扩展的选项,因为您可以发送此JAR

MANIFEST.MF这个文件中要注意的主要是你的主要课程。 当您运行JAR时,您将直接运行所需的类。

Main-Class: com.my.jar.TestSSL


将所需的库打包到JAR中:

它能做什么:
在这个选项中,Eclipse将:

  • 将所有引用的JAR打包到生成的JAR中。
  • 通过org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader使用Eclipse的JAR加载机制,你也可以在你生成的JAR中看到org.eclipse.jdt.internal.jarinjarloader包,这个包就在生成的JAR的根目录下。

当然这当你选择这个选项时会产生额外的成本,因为当你运行JAR时,你不是主类被执行,而是执行JarRsrcLoader ,这将加载你的主类和其他库,以及所有引用的库包装好了。 请参阅下面的MANIFEST.MF部分

MANIFEST.MF这个文件中要注意的主要是你的主要课程。 当您运行JAR时, JarRsrcLoader将运行并将继续工作。

Rsrc-Main-Class: com.cgi.tmi.TestSSL
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader


现在对于最后一个Eclipse导出选项 - “将所需的库复制到JAR旁边的子文件夹” ,我认为这不是一个非常可扩展的解决方案,因为这会强制您的文件系统依赖,所以我想不要这样做。

从运行控制台的Eclipse v / s运行时的性能差异:

当你从Eclipse运行应用程序时,它很安静,类似于第一个导出选项,其中Eclipse不需要在运行时解析和加载JAR。
然而,这是一个非常简单的观点,关键是考虑Eclipse JAR导出选项1 v / s选项2。


最后的话:

  • 使用“将所需的库提取到JAR”来导出JAR,您将看到实质性的性能提升。
    • 当您从控制台运行时,套接字连接很长时间是非常不可能的,因为JVM运行代码然后在从Eclipse和控制台运行时具有相同或非常可比的性能(在这两种情况下考虑相同的Java版本)。 你可能会因为打包的JAR性能而感觉到。 尝试提取JAR,你应该没问题。
  • 另外,请考虑您正在执行的日志记录量。 在运行时,根据配置,Eclipse可能会掩盖大量日志记录,从而节省您的i / o时间。
  • 理解如何从JAR类路径访问类 ,这就像从JAR引用类时的额外计算成本。

由于我们不知道JAR的确切结构,因此这是一个更一般的解释(假设您使用java -jar your_app.jar运行应用程序)。

case 将所需的库复制到JAR旁边的子文件夹中。

  • 如果需要加载类,则类加载器(在运行时JAR之后)首先检查your_app.jar以查找所需的类
  • 如果找不到该类,则遍历子文件夹中的所有JAR文件
  • 所有JAR文件都可以保存在文件系统缓存中以供进一步阅读

case将所需的库打包到JAR中

  • 如果需要加载类,则Eclipse类加载器JarRsrcLoader(在运行时JAR之后)首先检查your_app.jar以查找所需的类
  • 如果找不到类,则遍历所有嵌入的JAR文件,这意味着首先需要从your_app.jar解压缩才能读取内容
  • 提取的嵌入式JAR文件不会保存在文件系统缓存中以供进一步读取(因为它们不是文件系统中的文件)

如果你有更多的hugh嵌入式库JAR,这可能会导致类加载速度变慢(但只是第一次类加载器加载一个类)。

如果你比较的是outpout,你可以看到类加载的差异

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文件,可以提高性能。

这是因为当你采用“超级罐”方法时,一些元数据可能会丢失。

这只是一个例子,但是如果你下载这个这个 ,看看罐子里。 在同一META-INF文件夹中有一些具有相同名称的文件。

这些文件可能很重要,当eclipse为你重新打包时,他可能不会在合并这些文件方面做得不错。

这就是你可能会发生的事情。

在第二种方法中,您在main.jar中拥有所有依赖项jar。 因此除非需要,否则它不会加载任何依赖jar。 然而,在第3个选项的情况下,你的main.jar和其他依赖jar是独立的(不像第二种方式),因此可以加载连接并且可用。

尝试通过操作依赖jar来添加日志语句或syso以查看此工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM