JVM
Deep dive into the Java Virtual Machine — class loading, memory model, JIT compilation, and how the JVM turns bytecode into running software.
Last reviewed: January 2025
🔍 What Is It?
The JVM (Java Virtual Machine) is an abstract computing machine with three main subsystems:
Class Loader Subsystem
- →Bootstrap ClassLoader — loads core Java classes (java.lang.*) from rt.jar
- →Extension ClassLoader — loads from jre/lib/ext
- →Application ClassLoader — loads your application classes from classpath
- →Custom ClassLoaders — used by OSGi, application servers, hot-reload frameworks
Runtime Data Areas
- →Heap — shared across all threads; objects and arrays live here; GC manages it
- →Stack — per-thread; holds stack frames (local variables, operand stack, frame data)
- →PC Register — per-thread; points to current bytecode instruction
- →Native Method Stack — for native (C/C++) method calls via JNI
- →Metaspace — class structures, method metadata; grows dynamically
Execution Engine
- →Interpreter — executes bytecode instruction by instruction (slow but immediate)
- →JIT Compiler — compiles hot methods to native code (fast after warmup)
- →Garbage Collector — reclaims unreachable objects
JIT Compilation Tiers (HotSpot):
- →Tier 1: Simple JIT compilation
- →Tier 2: Limited profiling
- →Tier 3: Full profiling
- →Tier 4: Aggressive optimizations (C2 compiler)
🎯 Why Do We Need It?
The JVM gives Java several superpowers:
- →Platform independence — same bytecode runs on any OS with a JVM
- →Automatic memory management — no manual malloc/free, no dangling pointers
- →Security — bytecode verification prevents buffer overflows and type confusion
- →Performance — JIT compiler achieves near-C++ speed for long-running apps
- →Observability — JVM exposes metrics via JMX, making monitoring straightforward
For enterprise developers, understanding JVM internals is essential for diagnosing production incidents — OOM errors, GC pauses, thread deadlocks, and CPU spikes all require JVM knowledge to debug.
🏗️ What Happens in Real Projects?
You interact with the JVM daily even if you don't realize it:
- →Configuring heap in Docker:
ENV JAVA_OPTS="-Xmx2g -Xms2g" - →Analyzing heap dumps with Eclipse MAT or VisualVM when production OOMs
- →Reading thread dumps to diagnose deadlocks
- →Tuning GC when response times degrade
- →Using jstack, jmap, jstat for live JVM diagnostics
- →JVM flags in Kubernetes pod specs via environment variables
⚙️ How It Works
Class Loading (3 phases):
- Loading — finds and reads the .class file
- Linking — Verification (is bytecode valid?), Preparation (allocate static fields), Resolution (resolve symbolic references)
- Initialization — run static initializers, set static field values
Garbage Collection (simplified):
- Objects are allocated in Eden (young generation)
- Surviving objects move to Survivor spaces (S0/S1)
- Long-lived objects are promoted to Old Generation (tenured)
- Minor GC: cleans young generation (fast, frequent)
- Major/Full GC: cleans old generation (slow, infrequent)
JIT Compilation:
- Methods start interpreted
- HotSpot counts invocations (default: 10,000 for C2)
- Hot methods are compiled to native code
- Compiled code is cached in Code Cache
- Deoptimization can revert to interpreter if assumptions change
💻 Example
JVM Diagnostics in Practice — Essential JVM diagnostic commands every senior developer uses.
🤔 Common Confusions
❓ What is the difference between Stack and Heap?
Stack stores method call frames and primitive local variables — it is thread-private, fast, and automatically freed when the method returns. Heap stores all objects — it is shared across threads, managed by GC, and is where memory leaks occur. When you do new Object(), the object goes on the heap; the reference variable goes on the stack.
❓ Does the JVM run one thread or many?
Many. Beyond your application threads, the JVM runs GC threads, JIT compiler threads, finalizer thread, and signal dispatcher threads. A typical JVM process has 20–50 threads even for a simple app. Use jstack to see them all.
❓ What replaced PermGen in Java 8?
Metaspace. PermGen was a fixed-size region in the heap for class metadata — it caused frequent OutOfMemoryError: PermGen space. Metaspace is native memory (off-heap) and grows dynamically. It can still OOM if you have classloader leaks, but it is much more manageable.
🚨 Common Production Issues
GC pause causing request timeouts — long Stop-The-World pauses freeze all threads. Switch to G1GC or ZGC for low-latency requirements.
ClassLoader leak in application servers — redeploying WAR files without proper cleanup causes Metaspace to grow until OOM. Always test hot redeployment.
Code Cache full — JIT stops compiling new methods, app falls back to interpreter and slows down. Fix: -XX:ReservedCodeCacheSize=512m.
Native memory leak — off-heap memory (DirectByteBuffer, JNI) not released. Use -XX:NativeMemoryTracking=summary to diagnose.
✅ Best Practices
Use G1GC (-XX:+UseG1GC) for most applications; ZGC (-XX:+UseZGC) for sub-millisecond pause requirements.
Always capture thread dumps and heap dumps when production incidents occur.
Set -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof in all production JVMs.
Monitor GC pause time, not just frequency.
Use container-aware JVM flags in Docker: -XX:+UseContainerSupport (default Java 10+).
🏛️ Architect's Note
The JVM is a shared resource. In microservices, each service gets its own JVM process — this is intentional isolation. In a monolith on an app server (Tomcat, WildFly), multiple apps share one JVM, which means one misbehaving app can OOM the entire server. Container-per-service architectures solve this at the cost of more memory overhead. The JVM overhead (typically 200–500MB base) is the main reason Node.js and Go gained popularity for microservices.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
Core Java Interview Prep
Practice interview questions that connect directly to this technology track.
↗ Where this topic fits
Core Java Technology Guide
Return to the Core Java learning hub.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Sources
Sources / References
Use these Core Java references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide Core Java learning path
Browse the full Core Java topic collection and learning order on SplashRide.
Core Java Interview Prep
Practice Core Java interview questions and scenario-based answers.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Java Architecture
Understand how Java is designed to be platform-independent — the Write Once, Run Anywhere principle, and how the JDK, JRE, and JVM fit together.
Heap & Stack
Java memory model — Heap vs Stack, what lives where, and how understanding this prevents OutOfMemoryErrors and StackOverflowErrors.