thecodex.expert · The Codex Family of Knowledge
Java

JVM Internals

Java code doesn’t just run interpreted forever — the JVM watches which code paths actually run often, and compiles exactly those to real native machine code while the program keeps executing.

Java 21 LTS JIT compilation Last verified:
Canonical Definition

The JVM begins executing a program's bytecode via interpretation, which starts up quickly but runs slower than native code. Its Just-In-Time (JIT) compiler continuously profiles the running program, identifying “hot” methods and loops executed frequently enough to be worth the cost of compiling, then compiles specifically those paths to real native machine code, replacing the interpreted version while the program keeps running. Memory is organized into the heap (object storage, managed by the garbage collector) and the stack (one per thread, holding local variables and method call frames).

Interpretation, then JIT compilation

A long-running loop like this one is exactly the kind of code the JIT compiler targets — the first several thousand iterations run interpreted, and once the JVM's profiler determines this loop is genuinely "hot," it compiles it to native machine code on the fly, often making the same code noticeably faster later in the same run than it was at the start.

JavaHotLoop.java
long sum = 0;
for (long i = 0; i < 100_000_000; i++) {
    sum += i;   // runs interpreted at first; JIT-compiled to native code once "hot"
}

Heap and stack: where memory actually lives

Every thread gets its own stack; every object, regardless of which thread created it, lives on the single shared heap — which is exactly why objects need a garbage collector coordinating across threads, while stack memory is automatically reclaimed the instant a method returns.

Javamemory_example.java
void process() {
    int localCount = 0;              // lives on THIS thread's stack
    User user = new User("Alice");   // the User OBJECT lives on the shared heap;
                                       // only the reference "user" lives on the stack
}   // localCount and the reference are gone the instant process() returns;
    // the User object survives until the garbage collector determines nothing references it

Bytecode verification: a real security layer

Before executing any loaded class, the JVM's bytecode verifier checks that the bytecode is structurally valid and doesn't violate the type system or access rules — this matters because bytecode can come from an untrusted source (historically, applets downloaded over a network), and the verifier catches maliciously or accidentally corrupted bytecode before it ever runs.

Sources

1
Oracle. Java Virtual Machine Technology Overview, docs.oracle.com/en/java/javase/25/vm.