I am trying to migrate an existing command-line app to Spring boot and i have a weird problem.
The app works, but it seems to be very slow when started with
mvn spring-boot:run
It is not the app startup that is slow. There is a method which should fetch around 1.8 Mio records from the DB and create POJO's from result set. Normally this takes up to 40 sec.
With app started with maven it takes > 5 minutes.
If i start it with java -jar app.jar
it works fine/fast.
App is also fast when started in IntelliJ.
I am guessing it may be something with the classpath, but it is just a guess.
All i did in the app is to migrate some Singelton classes to @Components and add spring-boot-maven-plugin
Any ideas ?
CodePudding user response:
Found the problem:
TLDR: using spring boot 2.7 with (unsupported)Java 17
Hope this helps someone:
- The app i'm migrating is using Java 17.
- Spring boot 2.x does not support Java 17.
I actually made it work with Java 17 by configuring the maven plugins like this :
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<optimizedLaunch>false</optimizedLaunch>
</configuration>
</plugin>
By setting optimizedLaunch=false the plugin will omit -XX:TieredStopAtLevel=1
flag passed to JVM, which seems to be causing the issue.
Example:
slow:
/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home/bin/java -XX:TieredStopAtLevel=1 -cp A_LOT_OF_DEPS_HERE com.myapp.SpringApp
fast (no -XX:TieredStopAtLevel=1
):
/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home/bin/java -cp A_LOT_OF_DEPS_HERE com.myapp.SpringApp
What i still don't understand is why the code was so slow(spring-boot:run
) at some pattern matching(in my code), but not slow when ran with java(17) -jar my-app.jar
)
Anyway, as always, the device-behind-the-keyboard's fault (that's me)
CodePudding user response:
The JVM compiles your bytecode for better performance. There are multiple compilers, called C1 and C2.
The C1 compiler is moderately efficient, but compilation is fast.
The C2 compiler is more efficient, compiled code is faster, but this compiler is slower.
So, there is a tradeoff between the performance of the compiled code, and the time needed to compile the code.
If you disable the C2 compiler (by using -XX:TieredStopAtLevel=1, which tells the JVM to use C1 only), your application will start very fast. This is ideal for unit tests, or if your application will not work for a long time, because you won't loose time compiling with C2, and you don't have the time to actually benefit from code compiled by C2. Meanwhile, the code execution speed will not be optimal. This is what happens in your case: your application works over a long period of time, so it could benefit from C2 compilation. Unfortunately, you disabled it, which explains why your custom code is so slow. If you enable C2 compilation, you app will start a little bit slower, but you have time to benefit from C2, and you will obtain better performance over time.
Imagine you want to go to a bakery in order to purchase croissants. 1km. You can take the C1 Lada car: it's slow, but it starts instantly. You can take the C2 Tesla car: it's super fast, but you first have to charge its battery for 5h... You will take the C1 Lada car.
If you want to take a beach vacation 500km away, you will take the C2 Tesla car :)