JIT Compilation in the JVM

JIT compilation is the JVM's runtime process of turning frequently executed Java bytecode into optimized native machine code. It improves performance by using information gathered during execution to optimize hot code paths.

Java source is normally compiled into platform-independent bytecode stored in `.class` files. The JVM can initially execute that bytecode using interpretation and can identify methods or loops that execute frequently, commonly called hot code.

A JIT compiler compiles hot bytecode into native machine instructions for the current processor. Because compilation happens while the application is running, the compiler can use runtime information such as observed types, branch behavior, and call patterns when optimizing the generated code.

JIT optimizations can include method inlining, dead-code elimination, loop optimizations, and speculative optimizations. Some optimizations rely on assumptions that are usually true during execution; when an assumption becomes invalid, the JVM can deoptimize the compiled code and resume execution using a less optimized representation.

Modern JVMs can use multiple compilation tiers. Early execution can favor quick compilation, while code that proves to be especially hot can receive more aggressive optimization. This balances startup cost against long-running throughput.

JIT compilation has a runtime cost, so an application may experience warm-up before reaching its steady-state performance. This is important when evaluating Java services: benchmark results can differ substantially between cold startup and warmed-up execution.

Key points

Interview questions

What is JIT compilation in the JVM, and why does the JVM use it?

JIT compilation is the runtime compilation of selected JVM bytecode into native machine code. The JVM focuses compilation effort on code that executes frequently, because optimizing every method immediately would add unnecessary startup and compilation overhead. Runtime information also allows the compiler to make optimizations based on actual execution behavior. The result can be substantially better performance for long-running applications.

What is a hot method or hot loop?

A hot method or loop is code that executes frequently enough to justify optimization and compilation effort. The JVM collects execution information and uses it to determine which code is worth compiling or optimizing more aggressively. Hot code is important because JIT compilation has a cost, so compiling rarely executed code may not provide enough benefit. The exact thresholds and policies depend on the JVM and its configuration.

What is method inlining, and why is it useful to the JIT?

Inlining replaces a call to a small or suitable method with the method's body in the compiled code. This can remove call overhead, but its larger benefit is that subsequent optimizations can see across the former method boundary. For example, constant propagation or dead-code elimination may become possible after inlining. The JIT does not inline every method because code growth and other costs can make inlining counterproductive.

What is deoptimization in a JIT compiler?

Deoptimization occurs when previously compiled code depends on an assumption that is no longer valid. The JVM can invalidate or abandon that compiled path and transfer execution back to a safer representation, such as interpreted or less-optimized code. This allows the JIT to make speculative optimizations without requiring every assumption to remain permanently true. If execution later becomes stable under new conditions, the JVM may compile the relevant code again.

Why can a Java benchmark become faster after running for a while?

A JVM application can become faster after warm-up because frequently executed code may be compiled and optimized by the JIT. Early execution may include interpretation and compilation work, while later execution can benefit from optimized native code. Runtime profiling also accumulates information that can improve optimization decisions. Therefore, a benchmark that measures only initial execution can produce very different results from one that measures warmed-up steady-state behavior.