JVM Memory Model Explained
The JVM memory model defines where your objects and metadata live when a Java program runs. Understanding it helps you reason about OutOfMemoryError, tune heap size, and choose the right GC. This article explains the structure in plain terms and how it affects tuning and diagnostics.
Overview
The JVM divides memory into several areas. The ones that matter most for everyday coding are the heap (object storage) and metaspace (class metadata). Threads also have their own stacks for local variables and call frames.
The heap is shared by all threads. When you write new Object(), that object is allocated on the heap. The JVM manages the heap with a garbage collector; you only set its size (e.g. -Xmx). Metaspace holds class definitions and method metadata and lives outside the heap. Each thread's stack holds local primitives, references, and return addresses.
Example
A web app that caches user sessions in memory keeps those session objects on the heap:
Java// Session objects live on the heap Map<String, UserSession> sessions = new ConcurrentHashMap<>(); sessions.put(userId, new UserSession(...));
If you set -Xmx too low, under load the heap fills up and you get OutOfMemoryError: Java heap space. If you set it too high, GC pauses can get longer because there is more to collect. Tuning is a trade-off between headroom and pause time. Monitoring heap usage and GC logs tells you whether the limit is appropriate.
Core Mechanism / Behavior
- Heap: Divided into Young Generation (Eden + Survivors) and Old Generation. Most objects are allocated in Eden; if they survive a few GC cycles they are promoted to the Old Generation. The GC reclaims unreachable objects from both areas.
- Metaspace: Replaced the old PermGen. It holds class definitions, method metadata, and similar data. It lives outside the heap and can grow within limits.
OutOfMemoryError: Metaspaceindicates class metadata exhaustion (e.g. too many classes loaded or duplicate class loaders). - Stacks: One per thread. Each stack holds local primitives, references, and return addresses. Stack overflow usually means too deep recursion or too large local structures.
- Direct memory: Off-heap buffers (e.g. NIO direct buffers) are not in the heap. They can cause native OOM if overused and are not limited by
-Xmx.
Key Rules
- Confusing heap and metaspace:
OutOfMemoryError: Metaspaceis about class metadata, not regular objects. It often means too many classes loaded (e.g. many dynamic proxies or duplicate class loaders). - Ignoring direct memory: Off-heap buffers (e.g. NIO
DirectByteBuffer) are not in the heap. They can cause native OOM if overused. - One big heap for everything: Sometimes splitting apps or using a smaller heap with a responsive GC is better than one huge heap with long pauses.
- Tuning the heap and understanding where different kinds of data live helps you avoid OOM and manage GC behavior.
What's Next
For how the GC works and tuning, see GC basics and GC tuning principles. For diagnosing OOM, see OutOfMemoryError playbook.