JIT 101

May 25, 2020   ·   4 min read

java jvm jit compiler hotspot

JIT compilation and HotSpot are with no doubt some very complex topics. This blog is very short article touching on the topic in question; it was originally intended to be included in 97 Things Every Java Programmer Should Know. Contributions for the book had a limit on its length, thus a very short introduction into the HotSpot JIT. In the end, the book authors choose another piece of mine to be included but I thought it this might be interesting for the one or other person out there.

Compared to other compilers, javac avoids a lot of optimizations when compiling java source code to bytecode. While “Ahead-Of-Time” (AOT) compilation can do more heavyweight analysis of the source code, a dynamic compiler can take into account runtime statistics like the most used paths (hotspots) and advanced chipset features (e.g. which CPU instruction sets are available).

Enter the “Just-In-Time” (JIT) compiler. That means over time, the behaviour what and how to compile bytecode to native code changes. Initially, most bytecode is actually just interpreted (tier 0) which is rather slow. Once a code path is “hot” enough, C1 compiler kicks in (most of us know this by the -client flag). It is not as aggressive and allows for a faster initial startup. The C2 compiler (-server) uses more comprehensive analysis and is meant for long running processes. Since Java 7, the JVM uses a compilation mode called tiered compilation which seamlessly switches between the modes based on application behavior.

Initially, the compilers insert profiling probes into the bytecode to determine which code paths are the hottest (e.g. by invocation count), invariants (which types are actually used) and branch prediction. Once enough analytics are collected, the compilers actually start to compile bytecode to native code once they are “hot enough” (-XX:CompileThreshold), replacing the existing byte code step by step (mixed mode execution).

Starting with the hot path, one of the first things the compiler tries to achieve is constant folding. Using partial evaluation and escape analysis, the compiler will try to determine if certain constructs can be reduced to constants (e.g. the expression 3 * 5 can be replaced with 15). Another rather simple optimization is to avoid method calls by inlining methods into their call sites (if they are small enough).

Virtual method calls present a more complex problem. Generally, the best case is a monomorphic call, a method call that can be translated to a direct jump in assembly. Compare that to polymorphic calls, like an instance method whose type is not known in advance. The type invariants collected previously by the probes can help tremendously to identify which types are most often encountered within a code path.

The compiler optimizes aggressively using heuristics as well. In case a guess was actually wrong (e.g. the seemingly unused branch was called at some point), the compiler will deoptimize the code again and may revisit this path later using more profiling data.

Depending on the architecture the JVM is running on, the bytecode may not even be used at all. The HotSpot JVM uses a concept called “intrinsics” which is a list of well known methods that will be replaced with specific assembler instructions known to be fast. Good examples are the methods in java.lang.Math, System#arraycopy or Object#getClass (see @HotSpotIntrinsicCandidate).

Multi-threaded applications may as well benefit from the optimizations the JIT can do with synchronization locks. Depending on the locks used, the compiler may merge synchronized blocks together (Lock Coarsening) or even remove them completely if escape analysis determines that nobody else can lock on those objects (Lock Elision).

You can enable a lot of debug information about how the compiler decides what to do with your code using the feature flags like -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining. If you want to dive deeper into the world of the Hotspot JIT Compiler, have a look at JITWatch.

To have a real deep-dive into such topics, I can highly recommend the posts by Aleksey Shipilëv.



comments powered by Disqus