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 behavior of 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, the 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 has used a compilation mode called tiered compilation which seamlessly switches between the modes based on application behavior.