简体   繁体   English

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

[英]Strange behavior with different java application export option

I have java server application wich uses many libs (netty, guava, etc). 我有java服务器应用程序,它使用很多库(netty,guava等)。 I always export this application as one single .jar. 我总是将此应用程序导出为单个.jar。 When I run application in Eclipse, I didn't have any problems. 当我在Eclipse中运行应用程序时,我没有任何问题。 But if I start app in console (Windows, or Ubuntu, doesn't matter), I have strange problem: ALL connection processes via sockets last toooo long. 但是,如果我在控制台(Windows或Ubuntu,无关紧要)启动应用程序,我有一个奇怪的问题:通过套接字的所有连接进程持续太长时间。 For example, simple http connection via HttpAsync or others (rabbitmq connection, etc.) lasts 1-2 min. 例如,通过HttpAsync或其他人(rabbitmq连接等)的简单http连接持续1-2分钟。 But after connection completed, data sends/receives fast. 但连接完成后,数据发送/接收速度很快。 I can't figure what the problem. 我无法弄清楚是什么问题。 As mentioned before, I use Eclipse for development. 如前所述,我使用Eclipse进行开发。

As you know, you can export project 3 dif ways (in Eclipse): 如您所知,您可以导出项目3方式(在Eclipse中):

  1. Extract required libraries into JAR. 将所需的库提取到JAR中。
  2. Package required libraries into JAR. 将所需的库打包到JAR中。
  3. Copy required libraries into sub folder next to JAR. 将所需的库复制到JAR旁边的子文件夹中。

So, when I used 2 option, I had problem. 所以,当我使用2选项时,我遇到了问题。 When I switched to 3d option (all .jars in folder near main .jar), problem was solved. 当我切换到3d选项(main .jar附近的文件夹中的所有.jars)时,问题就解决了。

Generally there are no big difference between 2 and 3 option (in 2 all .jars just inside one jar). 通常情况下,2和3选项之间没有太大区别(2个所有.jars只在一个jar内)。 I thought that it was cause of extra time needed to load new classes in execution time from the jars. 我认为这是从jar开始在执行时加载新类所需的额外时间的原因。 But problem occurs not only at start, but for all new connections. 但问题不仅发生在开始,而且发生在所有新连接上。

Can someone explain this behavior? 有人可以解释这种行为吗?

UPD: Eclipse Luna. UPD: Eclipse Luna。 Doesn't matter what OS I'm using (Windows, or Ubuntu), even doesn't matter what jvm (tried with different Oracle jdk, even tried open jdk). 无论我使用什么操作系统(Windows,或Ubuntu),甚至无关紧要jvm(尝试使用不同的Oracle jdk,甚至尝试打开jdk)。

This all talks about difference in performance when packaging into JAR v/s extracting into JAR & difference in performance when running from Eclipse v/s running from console. 这一切都讨论了在从JAR v / s解压缩到JAR时的性能差异以及从从控制台运行的Eclipse v / s运行时的性能差异。

Difference in performance when packaging into JAR v/s extracting into JAR: 打包到JAR中的JAR v / s中的性能差异:

Extract required libraries into JAR: 将所需的库提取到JAR中:

What it does: 它能做什么:
In this option Eclipse will extract all the classes from the referenced JARs and package into the generated JAR. 在此选项中,Eclipse将从引用的JAR和包中提取所有类到生成的JAR中。

If you open the JAR then you will find that there are NO referenced JARs packaged but all the classes of referenced JARs are arranged as per the package structure and then packaged inside the JAR at root level. 如果打开JAR,那么您会发现没有打包引用的JAR,但所有引用的JAR类都按照包结构排列,然后在根级别打包在JAR中。 This brings the key difference in performance as compared to the "Packaging required libraries into a jar file" where there is additionally cost of runtime parsing and loading of JAR in memory etc.. “将所需包装到jar文件中”相比,这带来了性能上的关键差异,其中还存在运行时解析和在内存中加载JAR等成本。

When exporting as JAR through Eclipse then it is best option if performance is concern. 当通过Eclipse导出为JAR时,如果关注性能,则最好选择。 Also this is scalable option because you can ship this JAR 这也是可扩展的选项,因为您可以发送此JAR

MANIFEST.MF Main thing to note in this file is you main class. MANIFEST.MF这个文件中要注意的主要是你的主要课程。 When you run the JAR you are directly running the class you need. 当您运行JAR时,您将直接运行所需的类。

Main-Class: com.my.jar.TestSSL


Package required libraries into JAR: 将所需的库打包到JAR中:

What it does: 它能做什么:
In this option Eclipse will: 在这个选项中,Eclipse将:

  • package all the referenced JARs into the generated JAR. 将所有引用的JAR打包到生成的JAR中。
  • employ Eclipse's JAR loading mechanism through org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader and you can also see org.eclipse.jdt.internal.jarinjarloader package into your generated JAR and this package is just under the root directory of the generated JAR. 通过org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader使用Eclipse的JAR加载机制,你也可以在你生成的JAR中看到org.eclipse.jdt.internal.jarinjarloader包,这个包就在生成的JAR的根目录下。

Now of course this is the additional cost which comes when you choose this option because when you run the JAR then it is not you main class getting executed but JarRsrcLoader will be executed which will load your main class and other libraries, and all the referenced libraries are packaged. 当然这当你选择这个选项时会产生额外的成本,因为当你运行JAR时,你不是主类被执行,而是执行JarRsrcLoader ,这将加载你的主类和其他库,以及所有引用的库包装好了。 See MANIFEST.MF section below 请参阅下面的MANIFEST.MF部分

MANIFEST.MF Main thing to note in this file is you main class. MANIFEST.MF这个文件中要注意的主要是你的主要课程。 When you run the JAR, JarRsrcLoader will run and will do further job. 当您运行JAR时, JarRsrcLoader将运行并将继续工作。

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


Now for last Eclipse export option - "Copy required libraries into sub folder next to JAR" , I don't think it is a very scalable solution to consider because this imposes your file system dependency, so I would say don't do it. 现在对于最后一个Eclipse导出选项 - “将所需的库复制到JAR旁边的子文件夹” ,我认为这不是一个非常可扩展的解决方案,因为这会强制您的文件系统依赖,所以我想不要这样做。

Difference in performance when running from Eclipse v/s running from console: 从运行控制台的Eclipse v / s运行时的性能差异:

When you run application from Eclipse then it is quiet similar to 1st export option where Eclipse doesn't need to parse and load JARs at runtime and all. 当你从Eclipse运行应用程序时,它很安静,类似于第一个导出选项,其中Eclipse不需要在运行时解析和加载JAR。
This is however a very trivial point, key is the consideration of Eclipse JAR export option 1 v/s option 2. 然而,这是一个非常简单的观点,关键是考虑Eclipse JAR导出选项1 v / s选项2。


Final words: 最后的话:

  • Use "Extract required libraries into JAR" for exporting JAR and you will see substantial performance gain. 使用“将所需的库提取到JAR”来导出JAR,您将看到实质性的性能提升。
    • It is highly improbable that your socket connections are lasting long when you run from console because JVM runs code then it would have same or very comparable performance when running from Eclipse and console (considering same Java versions in both case). 当您从控制台运行时,套接字连接很长时间是非常不可能的,因为JVM运行代码然后在从Eclipse和控制台运行时具有相同或非常可比的性能(在这两种情况下考虑相同的Java版本)。 You could be feeling because of packaged JAR performance. 你可能会因为打包的JAR性能而感觉到。 Try extracted JAR and you should be fine. 尝试提取JAR,你应该没问题。
  • Also, consider the amount of logging you are doing. 另外,请考虑您正在执行的日志记录量。 When running through, depending upon configuration Eclipse may mask a lot of logging and hence saving you i/o time. 在运行时,根据配置,Eclipse可能会掩盖大量日志记录,从而节省您的i / o时间。
  • Do understand how classes are accessed from JAR class path , which is like additional computational cost when you are referencing classes from JAR. 理解如何从JAR类路径访问类 ,这就像从JAR引用类时的额外计算成本。

As we don't know the exact structure of your JAR here is a more general explanation (assumed you run your application with java -jar your_app.jar ). 由于我们不知道JAR的确切结构,因此这是一个更一般的解释(假设您使用java -jar your_app.jar运行应用程序)。

case Copy required libraries into sub folder next to JAR. case 将所需的库复制到JAR旁边的子文件夹中。

  • if a class needs to be loaded the class loader (after the runtime JAR) first checks your_app.jar to find a required class 如果需要加载类,则类加载器(在运行时JAR之后)首先检查your_app.jar以查找所需的类
  • if the class is not found it traversed over all JAR files in the subfolder 如果找不到该类,则遍历子文件夹中的所有JAR文件
  • all JAR files could be kept in the filesystem cache for further reading 所有JAR文件都可以保存在文件系统缓存中以供进一步阅读

case Package required libraries into JAR case将所需的库打包到JAR中

  • if a class needs to be loaded the Eclipse class loader JarRsrcLoader (after the runtime JAR) first checks your_app.jar to find a required class 如果需要加载类,则Eclipse类加载器JarRsrcLoader(在运行时JAR之后)首先检查your_app.jar以查找所需的类
  • if the class is not found it traversed over all embedded JAR files, which means as first they need to be decompressed from your_app.jar before the content can be read 如果找不到类,则遍历所有嵌入的JAR文件,这意味着首先需要从your_app.jar解压缩才能读取内容
  • the extracted embedded JAR files are not kept in the filesystem cache for further reading (as they are not files in the filesystem) 提取的嵌入式JAR文件不会保存在文件系统缓存中以供进一步读取(因为它们不是文件系统中的文件)

If you have a bigger number of hugh embedded library JARs this might lead in a slow down of class loading (but only for the first time a class is loaded by a class loader). 如果你有更多的hugh嵌入式库JAR,这可能会导致类加载速度变慢(但只是第一次类加载器加载一个类)。

You can see the difference in the class loading if you compare the outpout of 如果你比较的是outpout,你可以看到类加载的差异

java -verbose:class -jar your_app_external_library_jars.jar

with

java -verbose:class -jar your_app_embedded_library_jars.jar

The performance might be improved by generating an INDEX.LIST file for each JAR file (eg your_app.jar and the embedded library JARs). 通过为每个JAR文件(例如your_app.jar和嵌入式库JAR)生成INDEX.LIST文件,可以提高性能。

It happens because when you go with "uber jar" approach, some metadata might be lost. 这是因为当你采用“超级罐”方法时,一些元数据可能会丢失。

It's just an example, but if you download this and this , take a look inside the jar. 这只是一个例子,但是如果你下载这个这个 ,看看罐子里。 There are a few files with the same name in the same META-INF folder. 在同一META-INF文件夹中有一些具有相同名称的文件。

Those files might be important, and when eclipse repackages things for you, he might not be the doing a decent job on merging such files. 这些文件可能很重要,当eclipse为你重新打包时,他可能不会在合并这些文件方面做得不错。

That is what might be happening to you. 这就是你可能会发生的事情。

In the 2nd approach, You have all dependency jars in the main.jar. 在第二种方法中,您在main.jar中拥有所有依赖项jar。 So it won't load any of the dependency jars unless required. 因此除非需要,否则它不会加载任何依赖jar。 Whereas, in case of 3rd option, your main.jar and other dependency jars are independent (unlike 2nd way), and hence gets loaded for connections and is available. 然而,在第3个选项的情况下,你的main.jar和其他依赖jar是独立的(不像第二种方式),因此可以加载连接并且可用。

try adding a log statement or syso by manipulating a dependency jar to see this working. 尝试通过操作依赖jar来添加日志语句或syso以查看此工作。

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

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