Guide
What is the difference between the interpreter and the JIT compiler?
How HotSpot runs bytecode: the interpreter executes it first, profiling finds hot methods, and the C1 and C2 JIT compilers turn them into machine code, with real PrintCompilation output.
Interpreter
JVM specificationExecutes bytecode instruction by instruction; in HotSpot each bytecode is a pre-generated machine-code template.
Simply put
The interpreter reads one bytecode instruction, does what it says, then moves to the next. It can start immediately, without waiting for any compilation.
What a developer sees
Every Java program starts out interpreted. HotSpot’s is a template interpreter: at start-up it generates a small piece of machine code for each bytecode, then runs a method by jumping from template to template.
Code that runs once or rarely, such as start-up, configuration and error handling, may never leave the interpreter. -Xint makes HotSpot interpret everything, which makes long-running programs dramatically slower and shows how much the JIT compiler does.
Picture it
An interpreter at a meeting, translating each sentence as soon as it is spoken.
- Each sentence
Each bytecode instruction
- Translating as it goes
Executing one instruction at a time, with no preparation
- The listener
The processor (CPU)
Where the picture stops working
HotSpot’s interpreter does not work anything out afresh: it runs a ready-made machine-code template for each kind of instruction. It also keeps counts and profiles while it runs, which the JIT compiler uses later.
Deep dive: the specification and HotSpot in detail
JVMS §2.11 describes the conceptual loop: fetch an opcode, fetch its operands, execute the action, repeat. HotSpot's template interpreter is not a switch loop in C++: at VM start-up it generates a small machine-code template for every bytecode (TemplateTable) and dispatches from one to the next through a table indexed by opcode. It keeps the bytecode pointer, locals pointer and top-of-stack value in registers, increments invocation and back-edge counters, records profiles, and polls for safepoints at returns and loop back-edges. The generated interpreter lives in the code cache.
The specification requires
Conceptual fetch-decode-execute loop over JVM instructions.
HotSpot (JDK 25) does
HotSpot: template interpreter generated at start-up, with register-cached state and profiling.
Common misconceptions
The interpreter parses Java source or text.
It executes bytecode from class files. Source code never reaches the JVM.
HotSpot interprets with a big `switch` statement.
Standard builds use generated machine-code templates. The C++
BytecodeInterpreterexists only for the Zero port, which has no JIT.
Sources
- SpecJVMS §2.11 Instruction Set Summary(opens in a new tab)
Opcodes, operands and the inner loop of a JVM interpreter. · Java SE 25
- SourcetemplateInterpreter.hpp(opens in a new tab)
HotSpot's template interpreter, generated at VM startup. · JDK 25 GA (tag jdk-25-ga)
- SourcetemplateTable.hpp(opens in a new tab)
The per-bytecode machine-code templates. · JDK 25 GA (tag jdk-25-ga)
JIT compiler
HotSpot · JDK 25Compiles hot methods to native machine code while the program runs, guided by profiles.
Simply put
The just-in-time compiler watches for code that runs a lot and translates it into native machine code for your CPU while the program is running. From then on, that code runs much faster than in the interpreter.
What a developer sees
HotSpot counts how often each method runs. Hot methods are compiled to machine code while your program keeps running: first by C1 (tier 3, quick, with profiling), then by C2 (tier 4, heavily optimized). -XX:+PrintCompilation prints each compilation.
Because of this warm-up, the first seconds of a program run slower. Benchmark with a harness such as JMH rather than with a single timed loop.
Picture it
A musician who notices which pieces are requested every night and learns those by heart, instead of reading them from the sheet music each time.
- The sheet music
Bytecode
- Reading from the sheet
Interpreting
- Learning a piece by heart
Compiling a method to machine code
- Requested every night
Hot code, found by counting calls and loops
Where the picture stops working
The JIT can also unlearn: if an assumption behind compiled code stops being true (for example, a new subclass is loaded), the code is thrown away and the method runs in the interpreter until it is compiled again. And it practises in the background while the program keeps running.
Deep dive: the specification and HotSpot in detail
HotSpot uses tiered compilation (compilationPolicy.hpp): level 0 interpreter; 1 C1 without profiling; 2 C1 with invocation and back-edge counters; 3 C1 with full profiling; 4 C2. The common path is 0 → 3 → 4; trivial methods go to level 1; when the C2 queue is long, methods may take 0 → 2 → 3 → 4. Transitions are governed by thresholds such as Tier3InvocationThreshold (200) and Tier4InvocationThreshold (5000), scaled by compiler-queue load. Compilation runs in background compiler threads (3 in our measured run on 4 CPUs) while the application keeps executing.
OpenJDK 25 builds ship C1 and C2; GraalVM distributions can plug in the Graal compiler instead through JVMCI.
$ java -XX:+PrintCompilation -cp . Hot | grep Hot::Verified with OpenJDK 25.0.4 · Linux x64; columns: time (ms), compile id, flags (% = OSR), tier, method
Common misconceptions
The JIT compiles the whole program at start-up.
It compiles individual methods (and loops, via OSR) once they are hot. Most code is never compiled.
All JVMs use C1 and C2.
C1/C2 and tiered compilation are HotSpot design choices. Other JVMs use different compilers, and some run ahead-of-time compiled code.
Sources
- SourcecompilationPolicy.hpp(opens in a new tab)
Tiered compilation levels 0–4 and the thresholds that move methods between them. · JDK 25 GA (tag jdk-25-ga)
- SourcecompileBroker.hpp(opens in a new tab)
Compile queues and compiler threads. · JDK 25 GA (tag jdk-25-ga)
- SpecJVMS §2.13 Public Design, Private Implementation(opens in a new tab)
Implementations may translate bytecode to native code and choose their own internals. · Java SE 25
- JEPJEP 410: Remove the Experimental AOT and JIT Compiler(opens in a new tab)
Graal-based compilers were removed from OpenJDK builds. · JDK 17
Tiers and deoptimization
- Tiered compilation
HotSpot's step-by-step approach: interpret first, then compile quickly, then compile thoroughly.
Tiered compilation in the glossaryFive levels: 0 interpreter, 1 C1 without profiling, 2 C1 with counters, 3 C1 with full profiling, 4 C2. The usual path is 0 → 3 → 4, governed by thresholds such as
Tier3InvocationThreshold(200) andTier4InvocationThreshold(5000).- Deoptimization
Falling back from optimized code to the interpreter when an assumption turns out wrong.
Deoptimization in the glossaryHotSpot reconstructs interpreter frames from compiled code's debug information when an is hit or a dependency is invalidated, then continues in the interpreter; the method may be recompiled later.