Strange behavior with various java application export options

I have a Java server application that uses many libs (netty, guava, etc.). I always export this application as one .jar. When I run the application in Eclipse, I had no problems. But if I run the application in the console (Windows or Ubuntu, it does not matter), I have a strange problem: ALL connection processes through sockets are long long. For example, a simple http connection via HttpAsync or others (connection with a rabbit, etc.) lasts 1-2 minutes. But after the connection is completed, data is quickly sent / received. I can’t understand what the problem is. As mentioned earlier, I use Eclipse for development.

As you know, you can export a project 3 times (in Eclipse):

  • Extract the required libraries in the JAR.
  • The package requires a library in the JAR.
  • Copy the required libraries to a subfolder next to the JAR.

So, when I used option 2, I had a problem. When I switched to the 3d option (all .jars in the folder next to the main .jar), the problem was resolved.

As a rule, there is not much difference between option 2 and 3 (in 2 of all .jars only inside one jar). I thought this was the reason for the extra time it took to load new classes at runtime from jars. But the problem arises not only at startup, but for all new connections.

Can someone explain this behavior?

UPD: Eclipse Luna. It doesn’t matter which OS I use (Windows or Ubuntu) it doesn’t even matter that jvm (tried with another Oracle jdk, even tried to open jdk).

+6
source share
4 answers

All this speaks of the difference in performance when packaging in JAR v / s, extraction in the JAR and the difference in performance when starting from Eclipse v / s launched from the console.

Difference in performance when packaging in JAR v / s in JAR:

Extract the required libraries in the JAR:

What does he do:
In this option, Eclipse will retrieve all classes from the specified JARs and packages in the generated JAR.

If you open the JAR, you will find that there are no JAR packages that are not referenced, but all classes of related JAR packages are ordered according to the structure of the package, and then packaged inside the JAR at the root level. This brings a key performance difference compared to the “required jar file packaging libraries”, which additionally costs the deployment and loading of JARs in memory, etc.

When exporting to JARs through Eclipse, performance is the best option. It is also a scalable parameter because you can send this JAR

MANIFEST.MF The main thing in this file is the main class. When you run the JAR, you directly run the class that you need.

Main-Class: com.my.jar.TestSSL 


Package of required libraries in the JAR:

What does he do:
In this version, Eclipse will:

  • pack all the specified JAR files into the generated JAR.
  • use the Eclipse JAR loading mechanism through org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader , and you can also see the org.eclipse.jdt.internal.jarinjarloader package in your generated JAR, and this package is only under the root directory of the generated JAR.

Now, of course, this is an additional cost that arises when you select this parameter, because when you run the JAR, then you are not executing the main class, and the JarRsrcLoader will be executed, which will load your main class and other libraries, and all the indicated libraries packaged. See section MANIFEST.MF below

MANIFEST.MF The main thing in this file is the main class. When you start the JAR, the JarRsrcLoader will start and continue.

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


Now for the last Eclipse export option - “Copy the necessary libraries to a subfolder next to the JAR”, I don’t think this is a very scalable solution because it imposes a dependency on your file system, so I would say do it.

The difference in performance when starting from Eclipse v / s launched from the console:

When you start the application from Eclipse, it quietly looks like the 1st export option, where Eclipse does not need to parse and load the JAR at runtime and that’s it.
This, however, is a very trivial point, the key is to consider the Eclipse JAR 1 v / s export option.


Final words:
  • Use the "Extract required libraries in JAR" to export the JAR, and you will see a significant increase in performance.
    • It is very unlikely that your socket connections will last long when you start the console, because the JVM runs the code, then it will have the same or very comparable performance when working with Eclipse and the console (given the same versions of Java in both cases). You may feel due to JAR's packaged performance. Try extracting the JAR and everything will be fine.
  • Also consider the amount of logging you are doing. When launched, depending on the configuration, Eclipse can mask a lot of logging and therefore save I / O time.
  • Understand how classes access from the path of a JAR class , which is like an extra computational cost when you refer to classes from a JAR.
+2
source

As we do not know, the exact structure of your JAR here is a more general explanation (it is assumed that you start the application using java -jar your_app.jar ).

case Copy the required libraries to a subfolder next to the JAR.

  • If the class needs to be loaded, the class loader (after starting the JAR) first checks your_app.jar to find the required class
  • If the class is not found, it moves through all the JAR files in the subfolder
  • All JAR files can be stored in the file system cache for further reading.

case Required for library package in JAR

  • If the class needs to be loaded, the Eclipse class loader JarRsrcLoader (after starting the JAR) first checks your_app.jar to find the required class
  • If the class is not found, it moves through all the built-in JAR files, which means that they must first be unpacked with your_app.jar before the contents can be read
  • the extracted embedded JAR files are not stored in the file system cache for further reading (since they are not files in the file system)

If you have more JAR modules with the built-in hugh library, this can slow down the loading of classes (but only the first time the class is loaded by the class loader).

You can see the difference in class loading if you compare outpout

 java -verbose:class -jar your_app_external_library_jars.jar 

with

 java -verbose:class -jar your_app_embedded_library_jars.jar 

Performance can be improved by creating an INDEX.LIST file for each JAR file (for example, your_app.jar and the built-in JAR library).

+3
source

This is because when you go with the uber jar approach, some metadata may be lost.

This is just an example, but if you download this and this , look inside the jar. In the same META-INF folder there are several files with the same name.

These files can be important, and when eclipse repackages things for you, it may not do the decent job of merging such files.

This is what can happen to you.

+1
source

In the second approach, you have all the dependency bans in main.jar. Therefore, he will not download any of the cans of addiction, if this is not required. Considering that in the case of the 3rd option, your main.jar and other dependency banks are independent (unlike the second method) and, therefore, are loaded for connections and available.

try adding a log statement or syso by manipulating the dependency jar to see how it works.

0
source

All Articles