JIT Compilation

Interpreter + JIT

javac produces bytecode. The JVM first interprets it. Methods that run often ("hot") are compiled to native machine code by the JIT (Just-In-Time) compiler. HotSpot uses tiered compilation:

level 0  interpreter            ── counts calls and loop iterations
level 3  C1 compiler (quick)    ── compiled fast, with profiling
level 4  C2 compiler (optimized)── uses the profile: inlining, escape analysis,
                                   loop unrolling, dead-code removal
         deoptimize ◄────────── if an assumption breaks (new subclass loaded...)

Key Optimizations

OptimizationWhat it does
Inliningcopies small hot methods (getters) into the caller: no call overhead
Escape analysisan object that never leaves a method may be allocated on the stack or removed entirely
Loop unrolling / vectorizationfewer jumps, SIMD instructions
Devirtualizationturns an interface call with one implementation into a direct call
Lock elisionremoves locks on objects no other thread can see

Warm-up

Because of JIT, Java apps get faster after running a while (warm-up). That is why micro-benchmarks with System.nanoTime() often lie — use JMH. For fast startup (serverless, CLIs) look at GraalVM native image, CDS (class data sharing) and CRaC.

Example

See which methods get compiled:

java -XX:+PrintCompilation Main

Example output

     45    1       3       java.lang.String::hashCode (60 bytes)
     52    7       4       java.lang.String::charAt (25 bytes)
    118   41 %     4       Main::sumLoop @ 4 (27 bytes)
    120   42       4       Main::sumLoop (27 bytes)