Heap & Stack
Java memory model — Heap vs Stack, what lives where, and how understanding this prevents OutOfMemoryErrors and StackOverflowErrors.
Last reviewed: January 2025
🔍 What Is It?
Stack Memory:
- →One stack per thread
- →Stores: method call frames, local variables (primitives and object references), return addresses
- →LIFO (Last In, First Out) — method frames pushed on call, popped on return
- →Fixed size (default: 512KB–1MB per thread, configurable with -Xss)
- →Automatic management — no GC needed, memory freed when method returns
- →Fast: simple pointer increment/decrement
- →StackOverflowError when too deep (usually infinite recursion)
Heap Memory:
- →One heap shared by all threads
- →Stores: all objects (new Foo()), arrays, String pool (part of heap)
- →Managed by Garbage Collector
- →Configurable size: -Xms (initial), -Xmx (maximum)
- →Generational structure:
- Young Generation (Eden + Survivor spaces): new objects allocated here
- Old Generation (Tenured): long-lived objects promoted here
- Minor GC: cleans young generation (fast, frequent)
- Major/Full GC: cleans old generation (slow, rare)
Metaspace (Java 8+, was PermGen):
- →Stores class metadata, method bytecode, static variables
- →Native memory (not part of heap)
- →Grows dynamically (no fixed size by default)
Code Cache:
- →Stores JIT-compiled native code
- →Fixed size, configurable with -XX:ReservedCodeCacheSize
🎯 Why Do We Need It?
Understanding Heap vs Stack is essential for:
- →Diagnosing OutOfMemoryError — is it heap (too many objects) or stack (too deep recursion)?
- →Understanding GC — only heap objects are garbage collected
- →Memory leak analysis — objects on heap that are never GC'd
- →Thread safety — stack variables are thread-private (safe); heap objects are shared (need synchronization)
- →Performance tuning — large heaps need tuning; deep stacks need -Xss adjustment
🏗️ What Happens in Real Projects?
In production:
- →OOM: Java heap space — increase -Xmx or find memory leak with heap dump
- →OOM: Metaspace — classloader leak, increase -XX:MaxMetaspaceSize
- →StackOverflowError — infinite recursion, increase -Xss or fix the recursion
- →High GC pause — too many short-lived objects, tune young generation size
⚙️ How It Works
Object Allocation:
void processOrder(Long orderId) {
// Stack: frame for processOrder
// Stack: orderId reference (Long object is on heap)
Order order = new Order(orderId); // Order object on HEAP
// Stack: 'order' variable (reference to heap object)
int total = calculateTotal(order); // Stack: new frame for calculateTotal
// Stack: 'total' int value
// When processOrder returns:
// Stack frame popped — 'order' reference gone
// Order object on heap becomes eligible for GC (if no other references)
}Escape Analysis (JIT optimization):
The JIT compiler can detect that some objects never "escape" the method — they are not returned, stored in fields, or passed to other threads. These objects may be allocated on the stack instead of the heap, eliminating GC pressure. This is transparent to developers but explains why Java's performance is better than naive analysis suggests.
💻 Example
Memory Allocation in Practice — How to diagnose and fix memory issues in production Java.
🤔 Common Confusions
❓ Are all objects always on the heap?
Mostly yes, but JIT escape analysis can allocate non-escaping objects on the stack. Also, Java 21+ with Valhalla (value types) will allow some objects to be stored inline without heap allocation. For practical purposes, assume new Object() allocates on the heap.
❓ What is the difference between OutOfMemoryError: Java heap space and OutOfMemoryError: Metaspace?
Heap space OOM: too many objects on the heap — increase -Xmx or fix memory leaks. Metaspace OOM: too many classes loaded — usually a classloader leak (common in app servers with hot deployment) or reflection-heavy frameworks. Increase -XX:MaxMetaspaceSize or fix the classloader leak.
❓ Why does StackOverflowError occur?
Each method call creates a new stack frame. StackOverflowError occurs when the stack has no more space for new frames. The most common cause is infinite recursion (a method calling itself without a base case). Fix: find the recursion and add a proper base case, or convert to iteration. Rarely, very deep legitimate recursion needs -Xss to increase stack size.
🚨 Common Production Issues
Memory leak — objects held in static collections (caches, listeners) that are never removed. Use weak references or bounded caches (Caffeine) for caches.
Large heap causing long GC pauses — with G1GC, heaps up to 32GB are manageable. For larger heaps, use ZGC or Shenandoah which have sub-millisecond pauses.
Thread stack memory — each thread uses -Xss memory for its stack. 1000 threads * 512KB = 500MB just for stacks. With virtual threads (Java 21), stack memory per thread is much smaller.
✅ Best Practices
Set -Xms equal to -Xmx in production to avoid heap resizing pauses.
Always set -XX:+HeapDumpOnOutOfMemoryError in production.
Use G1GC for most applications; ZGC for latency-sensitive large heaps.
Monitor GC activity with -Xlog:gc* and alert on high GC pause times.
Use weak references (WeakHashMap) for caches to allow GC to reclaim entries.
Profile memory usage before and after major releases to catch regressions.
🏛️ Architect's Note
Memory sizing is not a one-time decision. As your application grows (more users, more data, more features), heap requirements change. Establish memory monitoring from day one: track heap usage over time, GC pause frequency, and Metaspace size. Set alerts at 80% heap usage. A heap dump from production (taken during normal operation) analyzed in Eclipse MAT is the most effective way to understand your application's memory profile.
💬 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.
JVM
Deep dive into the Java Virtual Machine — class loading, memory model, JIT compilation, and how the JVM turns bytecode into running software.
Garbage Collection
Java Garbage Collection — how GC works, G1GC vs ZGC, tuning for production, and diagnosing GC-related performance issues.