JVM Architecture
The Java Virtual Machine (JVM) is the runtime that executes Java bytecode. Understanding its architecture helps with debugging, performance tuning, and writing efficient code.
JVM Structure
┌─────────────────────────────────────────┐
│ Class Loader Subsystem │
│ (Loading → Linking → Initialization) │
├─────────────────────────────────────────┤
│ Runtime Data Areas │
│ ┌─────────┐ ┌──────┐ ┌───────────────┐ │
│ │ Heap │ │ Stack│ │ Metaspace │ │
│ │ (shared)│ │(thread│ │ (class meta) │ │
│ └─────────┘ └──────┘ └───────────────┘ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ PC Register │ │ Native Method Stack│ │
│ └──────────────┘ └──────────────────┘ │
├─────────────────────────────────────────┤
│ Execution Engine │
│ ┌──────────┐ ┌─────┐ ┌──────────────┐ │
│ │Interpreter│ │ JIT │ │ Garbage │ │
│ │ │ │Compiler│ │ Collector │ │
│ └──────────┘ └─────┘ └──────────────┘ │
├─────────────────────────────────────────┤
│ Native Method Interface (JNI) │
└─────────────────────────────────────────┘
Class Loader Subsystem
Loads .class files into memory:
- Loading — Find bytecode and create a
Classobject - Linking — Verify, prepare, and resolve references
- Initialization — Execute static initializers
See Class Loading for details.
Runtime Data Areas
Heap (Shared)
Stores all object instances and arrays. Shared among all threads. Divided into:
- Young Generation — Eden + Survivor spaces (new objects)
- Old Generation — Long-lived objects
Configured with -Xms (initial) and -Xmx (maximum) heap size.
Stack (Per Thread)
Each thread has its own stack with stack frames — one per method call. Stores:
- Local variables
- Operand stack
- Reference to the constant pool
Stack overflow: StackOverflowError. Default size: -Xss (typically 1 MB).
Metaspace (Since Java 8)
Replaced PermGen. Stores class metadata — class definitions, method data, constant pools. Grows dynamically, limited by available native memory (-XX:MaxMetaspaceSize).
PC Register (Per Thread)
Points to the current instruction being executed.
Native Method Stack
Supports native (non-Java) method calls via JNI.
Execution Engine
Interpreter
Executes bytecode instructions one by one. Slow but starts immediately.
JIT Compiler
Just-In-Time compiler translates hot bytecode to native machine code for faster execution. HotSpot identifies “hot spots” through profiling.
Common JIT compilers: C1 (client, fast compilation), C2 (server, aggressive optimization).
Garbage Collector
Automatically reclaims memory from unreachable objects. See Garbage Collection.
Execution Process
Source (.java) → javac → Bytecode (.class) → ClassLoader → JVM
↓
Execution Engine
(Interpreter + JIT)
↓
Native Machine Code
JVM Implementations
| JVM | Vendor |
|---|---|
| HotSpot | Oracle/OpenJDK (default) |
| GraalVM | Oracle — polyglot, native image |
| OpenJ9 | IBM/Eclipse — lower memory footprint |
| Azul Zing | Azul — low-latency GC |
Best Practices
- Understand heap vs stack — objects on heap, references and primitives on stack
- Monitor metaspace in applications with dynamic class loading
- Use
-Xmxto set appropriate heap limits in production - Profile before tuning — understand which runtime area is the bottleneck