How To Fix Java 21 Jit Compilation Slowness [Solved]

Problem Area Recommended Fix Expected Result
JIT Warm-up Lag Adjust Tiered Compilation levels Faster initial response times
Code Cache Exhaustion Increase ReservedCodeCacheSize Prevents JIT from shutting down
GC Interference Enable Generational ZGC Reduced CPU spikes during JIT
Cold Start Slowness Implement AppCDS Significant reduction in startup time

Java 21 JIT compilation performance tuning guide.

What is Java 21 JIT Compilation Slowness?

Java 21 JIT compilation slowness refers to a performance bottleneck where the Just-In-Time (JIT) compiler takes excessive time or CPU resources to optimize bytecode into native machine code. This is often noticed during the “warm-up” phase of an application.

In Java 21, while performance is generally superior, certain configurations of Tiered Compilation (C1 and C2 compilers) can lead to high CPU overhead. If the JIT compiler cannot keep up with the execution demand, the application runs on the slower interpreter for longer periods.

This slowness can also be caused by Code Cache fragmentation. When the memory area for stored compiled code fills up, the JIT compiler stops working, causing the application to revert to interpreted mode, which is significantly slower.

Step-by-Step Solutions

1. Optimize Tiered Compilation Levels

If your application experiences heavy CPU usage immediately after startup, you can limit the tiered compilation. Setting the stop level to 1 or 3 can reduce the load on the C2 compiler during critical startup phases.

# Limit tiered compilation to level 1 for faster warm-up
java -XX:TieredStopAtLevel=1 -jar your-app.jar

2. Increase the Reserved Code Cache

Java 21 applications, especially those using large frameworks like Spring Boot, can easily exceed the default Code Cache size. If this happens, JIT compilation is disabled. Increase the limit to ensure the compiler has enough space.

# Increase Code Cache to 512MB
java -XX:ReservedCodeCacheSize=512m -jar your-app.jar

3. Enable Generational ZGC

Java 21 introduced Generational ZGC, which significantly reduces the overhead of garbage collection. Since JIT compilation and GC compete for CPU cycles, enabling this can free up resources for faster JIT optimization.

# Enable the new Generational ZGC in Java 21
java -XX:+UseZGC -XX:+ZGenerational -jar your-app.jar

4. Utilize Application Class Data Sharing (AppCDS)

To bypass much of the JIT pressure during startup, use AppCDS. This allows the JVM to map metadata into memory from an archive, reducing the need for the JIT compiler to re-process classes on every launch.

# Step 1: Create a list of classes
java -Xshare:dump -XX:SharedClassListFile=classes.lst -XX:SharedArchiveFile=app.jsa -jar your-app.jar

# Step 2: Run with the archive
java -Xshare:on -XX:SharedArchiveFile=app.jsa -jar your-app.jar

5. Tune Compilation Thresholds

You can force the JIT compiler to trigger earlier or later by adjusting the invocation counters. Decreasing the threshold makes the JIT compiler start optimizing code sooner, which is useful for short-lived microservices.

# Decrease the threshold for C1 compilation
java -XX:Tier4InvocationThreshold=5000 -jar your-app.jar