JIT compilation and warm-up
Code starts interpreted, gets compiled once it is proven hot, and only then runs at full speed.
Open this lesson in the learning hubKey points
- Every method starts interpreted. Compiling everything up front would waste time on code that only runs twice.
- The JVM counts method invocations and loop back-edges. Cross a threshold and that method is queued for compilation.
- Compilation is tiered: C1 compiles quickly and adds profiling counters, then C2 uses that profile to compile really well.
- The profile lets the JIT bet - this call is always the same type, this branch never happens - and emit code for the common case.
- When a bet turns out wrong the JVM deoptimises: back to the interpreter, then recompile with better information.
- This is warm-up. Benchmark a cold JVM and you are mostly measuring the interpreter, not your code.
Example
public class Main {
static long mix(long h, int i) { return 31 * h + (i ^ (h >>> 7)); }
static long round(int n) {
long h = 1125899906842597L;
for (int i = 0; i < n; i++) { h = mix(h, i); }
return h;
}
public static void main(String[] args) {
long guard = 0;
System.out.println("round microseconds for the same 20,000 calls");
for (int r = 1; r <= 40; r++) {
long t0 = System.nanoTime();
guard += round(20_000);
long us = (System.nanoTime() - t0) / 1000;
if (r <= 4 || r == 10 || r == 20 || r == 40) {
System.out.printf("%5d %8d%n", r, us);
}
}
System.out.println("checksum " + guard);
System.out.println();
System.out.println("Round 1 is interpreted bytecode. C1 compiles it fast and rough,");
System.out.println("then C2 recompiles the hot path into genuinely good machine code.");
System.out.println("That is why a fresh JVM is slow for its first few thousand requests.");
}
}
The first thousand calls are not the ones worth timing.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the JVM course, and every lesson in it is listed on the JVM contents page.