JIT (Just-In-Time) is a dynamic compilation technology that converts bytecode or intermediate program representation into machine instructions directly during execution. In Android, the JIT compiler first appeared in version 2.2 Froyo as part of the Dalvik virtual machine and accelerated application execution by 2–5 times. According to Google, 2024, the modern JIT in ART combines interpretation with profiled compilation of hot methods.
Key Takeaways
Just-In-Time (JIT) is a compilation method in which source code or bytecode is converted into machine instructions not in advance (as with AOT), but at the moment of the first call to the corresponding section of the program. The term “Just-In-Time” means that compilation happens “just in time” — immediately before execution.
The concept of JIT has existed since the 1960s, but gained widespread adoption with the advent of the Java Virtual Machine in 1995. JIT allows combining the portability of bytecode (write once — run anywhere) with performance close to native code. In the Java HotSpot VM, the JIT compiler analyzes the executed code and compiles only the most critical sections, saving time and memory.
The JIT compiler receives bytecode as input, interprets it, and simultaneously collects statistics. When a certain code section (method, loop) is called frequently enough, JIT decides to compile it. The compiled machine code is stored in a cache — on subsequent calls, the already compiled version is used. This provides acceleration without having to compile the entire program.
// Example: a method becomes hot after multiple calls
public class HotMethod {
private int compute(int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += i * i;
}
return sum;
}
}
// Calling 500 times in a loop — JIT will compile compute
for (int t = 0; t < 500; t++) {
hot.compute(1000);
}
In Android, JIT compilation went through three phases of evolution. The first phase — Dalvik without JIT (Android 1.0–2.1): pure interpretation of DEX bytecode. The second phase — Dalvik with JIT (Android 2.2–4.4): the introduction of the JIT compiler, which accelerated applications by 2–5 times. The third phase — ART with hybrid JIT (Android 7.0+): the return of JIT in a new capacity.
JIT in Dalvik was implemented as a trace-based compiler. It analyzed not individual methods, but chains of instructions (traces) that are frequently executed sequentially. This allowed compiling entire execution paths, including multiple methods. This approach was effective for mobile processors with small instruction caches, as the compiled trace fit into the L1 cache.
Starting with Android 7.0 Nougat, ART uses method-based JIT — it compiles individual methods based on execution profiles. This JIT works significantly faster than Dalvik JIT: typical compilation time for one method is 0.5–1 ms compared to 3–5 ms in Dalvik. The compiled code is stored in a separate memory area (JIT code cache) rather than in the application heap, which reduces fragmentation.
| Parameter | Dalvik JIT | ART JIT |
|---|---|---|
| Type | Trace-based | Method-based |
| Compilation Speed | 3–5 ms/method | 0.5–1 ms/method |
| Compilation Threshold | ~200 calls | Dynamic |
| Code Cache | In application heap | JIT code cache |
| Profiling | Internal | External .prof files |
The central mechanism of JIT is hot method detection. Each method call increments an internal counter. When the counter crosses the threshold, the method is marked as “hot” and sent for compilation. In Dalvik, the threshold was fixed (~200 calls). In ART, counters are configured dynamically depending on the device’s available resources.
The compilation process includes several phases. The first — bytecode analysis: JIT examines the instruction stream and builds a data-flow graph. The second — optimization: inlining of small methods, dead code elimination, constant folding. The third — code generation: converting the optimized graph into machine instructions for a specific CPU architecture (ARM, ARM64, x86).
// Demonstration of inlining — JIT will inline the method body
public int inlineExample() {
return square(5);
}
private int square(int x) {
return x * x;
} // JIT will replace the call with return 5 * 5;
A special JIT technique — On-Stack Replacement (OSR). If a method contains a long loop that doesn’t finish for hundreds of iterations, JIT can compile the loop “on the fly” and replace the interpreted version with the compiled one right during execution. OSR is especially effective for computational tasks: rendering, image processing, cryptography.
JIT and AOT are two compilation approaches with opposing trade-offs. JIT sacrifices first-launch speed for compact distribution size and adaptability. AOT sacrifices installation time and disk space for maximum performance from the first second. Neither approach is absolutely better — the choice depends on the scenario.
The key advantage of JIT is adaptive optimization. JIT can use profile information unavailable to AOT: exact object types, actual call frequency, real branching patterns. This allows applying aggressive optimizations impossible with static compilation. For example, JIT can devirtualize method calls if only one receiver type is encountered in practice.
| Criterion | JIT | AOT |
|---|---|---|
| Installation Time | Instant | Depends on size |
| First Launch | Slower (warm-up) | Fast |
| Disk Space | Minimal | +15–30% |
| Adaptability | High | Low |
| CPU Usage | Spikes during compilation | Stable |
JIT compilation is preferable when fast deployment and disk space savings are important. In the context of mobile development, JIT is ideal for applications that are updated frequently (A/B testing, hotfixes). JIT is also convenient during development, when code is rebuilt dozens of times a day — each second saved on compilation accelerates the feedback loop.
JIT provides developers with a number of practical advantages. First — small APK size. With the JIT approach, only bytecode (DEX) is packaged in the APK, which takes 20–30% less space than compiled native code. For users with limited built-in storage, this is a significant advantage.
The second advantage is device adaptation. JIT compiles code taking into account the actual CPU architecture, RAM size, and current load. For example, on a device with 2 GB of RAM, JIT may compile less aggressively to save memory, while on a flagship with 12 GB, it can apply all possible optimizations. AOT compilation, on the other hand, fixes the decision at installation time.
Bytecode remains platform-independent, which simplifies application distribution. One APK works on ARM, ARM64, and x86 devices, and JIT generates native code for each architecture. The AOT approach would require either including multiple native code variants in the APK (increasing size) or compiling a separate version for each architecture.
The main disadvantage of JIT is warm-up delay. The user experiences slowdowns in the first seconds of the application while JIT compiles hot methods. In games, this manifests as stuttering in the initial levels. In applications with animations — jerky first transitions between screens.
The second disadvantage — power consumption. The compilation process heavily loads the CPU, increasing power consumption by 10–20% during the warm-up period. On battery-powered devices, this reduces battery life. It is especially noticeable in scenarios with frequent application restarts (multitasking with limited memory, where the system unloads and reloads processes).
Another issue — JIT cache fragmentation. The compiled code is stored in a continuous memory area. When new classes are loaded and additional methods are compiled, the cache becomes fragmented, increasing memory management overhead. In Dalvik, this problem was solved by periodic cache clearing; in ART, the JIT cache is allocated separately from the heap and uses its own defragmentation strategy.
The modern approach in ART — hybrid compilation, combining the strengths of JIT and AOT. During application installation, no compilation is performed — only bytecode verification (verify). This ensures fast installation and minimal space usage. The first launches run in interpretation mode with JIT compilation of hot methods — the user gets acceptable performance without long waiting times.
Simultaneously, a background profiler collects data about real usage. After 2–3 full application launches, the profile reaches sufficient completeness, and the system launches dex2oat to compile hot methods into native code. This operation is performed in the background when the device is not loaded (charging, screen off). After background AOT completes, the application achieves performance comparable to full AOT compilation.
# Forced start of background compilation
adb shell cmd package compile -m speed-profile -f com.example.app
# View compilation status
adb shell cmd package dump-profiles com.example.app
According to Google I/O 2017, hybrid compilation reduced application installation time by 30–50% compared to pure AOT. The disk space occupied on the system partition decreased by 20–30%. At the same time, performance after background compilation matches the level of full AOT. The only scenario where hybrid falls short of AOT is the first launch immediately after installation: the application runs in JIT mode and may be 10–15% slower.
Frequently Asked Questions
JIT is a way to speed up a program where code is translated into machine language not in advance, but in parts while running. The most frequent sections are compiled and cached, while rare ones remain in their original form.
JIT compiles code during execution, saving space and speeding up installation. AOT compiles all code in advance — the application starts faster but requires more disk space and installation time.
JIT wasn’t removed — it evolved. In Android 5.0, Dalvik with JIT was replaced by ART with pure AOT. In Android 7.0, JIT returned to ART as part of a hybrid system where it works together with background AOT compilation for optimal performance.
JIT increases power consumption by 10–20% during the warm-up period due to CPU load. After hot method compilation completes, power consumption returns to normal levels. ART’s hybrid mode minimizes these spikes through background compilation.
Yes, in scenarios with intensive computations. The user may notice slowdowns in the first seconds of application operation or at the beginning of a game. In modern versions of Android (8.0+), the hybrid mode minimizes this effect thanks to profiled compilation.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also