JVM Memory Architecture: Heap, Stack, Metaspace & Memory Leaks

When a Java application runs, the JVM organizes system RAM into distinct memory spaces defined by the Java Virtual Machine Specification. Mastering JVM memory management enables you to prevent OutOfMemoryError, diagnose memory leaks, and tune high-scale production systems.

JVM Memory Model: Heap, Stack, Metaspace, and Native Memory

1. The Five JVM Run-Time Data Areas

Memory AreaThread ScopeStored ContentsAssociated Error
Heap MemoryShared by All ThreadsAll class instances, objects, strings, arraysOutOfMemoryError: Java heap space
JVM StackPer-ThreadMethod call frames, local variables, primitive dataStackOverflowError
Metaspace (Java 8+)Shared by All ThreadsClass metadata, method bytecodes, constant pools (in native RAM)OutOfMemoryError: Metaspace
Program Counter (PC)Per-ThreadAddress of current executing JVM instructionNone
Native Method StackPer-ThreadC/C++ native method call frames via JNIStackOverflowError

2. Deep Dive: Heap Memory Architecture

Heap memory is divided into generational spaces based on the Weak Generational Hypothesis (the observation that most objects die very shortly after creation):

  • Young Generation:
    • Eden Space: Where newly created objects are first instantiated via new.
    • Survivor Spaces (S0 & S1 / From & To): Objects that survive minor garbage collection cycles are moved between S0 and S1, incrementing their age counter.
  • Old (Tenured) Generation: Objects that survive multiple GC cycles (default threshold: 15 cycles) are promoted to Tenured generation for long-term storage.

3. Stack Memory & Stack Frame Anatomy

Each active thread has its own Stack. Every time a method is invoked, the JVM pushes a new Stack Frame containing:

  1. Local Variable Array: Holds parameters and local variables.
  2. Operand Stack: Working memory where CPU arithmetic operations are staged.
  3. Frame Data: Holds reference to the Run-Time Constant Pool and exception handling dispatchers.

StackOverflowError vs OutOfMemoryError in Action

Case A: StackOverflowError (Infinite Recursion)

public class StackOverflowDemo {
    public static void recursiveBomb(int count) {
        // Pushes stack frames continuously without base condition
        recursiveBomb(count + 1);
    }

    public static void main(String[] args) {
        // Throws: Exception in thread "main" java.lang.StackOverflowError
        recursiveBomb(1);
    }
}

Case B: OutOfMemoryError: Java heap space

import java.util.ArrayList;
import java.util.List;

public class HeapOOMDemo {
    public static void main(String[] args) {
        List memoryLeakList = new ArrayList<>();
        // Rapidly fills the Heap with 10MB byte arrays
        while (true) {
            memoryLeakList.add(new byte[10 * 1024 * 1024]);
        }
        // Throws: java.lang.OutOfMemoryError: Java heap space
    }
}

Crucial JVM Memory Tuning Command-Line Flags

JVM FlagPurpose & Example
-Xms<size>Initial Heap size at JVM startup (e.g., -Xms2g for 2 Gigabytes)
-Xmx<size>Maximum allowable Heap size (e.g., -Xmx8g)
-Xss<size>Stack size per thread (default ~1MB, e.g., -Xss512k)
-XX:MaxMetaspaceSizeUpper boundary for native Metaspace (e.g., -XX:MaxMetaspaceSize=512m)
-XX:+HeapDumpOnOutOfMemoryErrorGenerates a diagnostic .hprof memory dump automatically when OOM occurs
BCA / University Exam Corner
  • Viva Question: What is a memory leak in Java if the Garbage Collector is automatic?
    Answer: A memory leak occurs when unneeded objects remain reachable through active GC Roots (such as static collections, unclosed streams, or forgotten listener callbacks). Because references still exist, the GC is not permitted to reclaim their memory, eventually causing an OutOfMemoryError.
  • Exam Distinction: Remember that primitives declared inside methods are stored directly on the Stack, while all Objects (and primitive arrays) are allocated on the Heap.

Frequently Asked Questions (FAQ)

Q1. What is the fundamental difference between Java Heap and Java Stack memory?
Stack memory is allocated per-thread for executing methods and storing local variables and primitive values. It operates in strict LIFO order and is fast and automatically freed when methods exit. Heap memory is shared across all threads and stores all instantiated Objects and arrays; it is managed by the Garbage Collector.
Q2. What replaced PermGen in Java 8 and why was the change made?
PermGen (Permanent Generation) was completely removed in Java 8 and replaced by Metaspace. PermGen had a fixed contiguous size inside the JVM heap, frequently causing java.lang.OutOfMemoryError: PermGen space. Metaspace resides in off-heap native OS memory and expands automatically by default, avoiding premature exhaustion.
Q3. What is the difference between StackOverflowError and OutOfMemoryError?
StackOverflowError occurs when a thread exhausts its allocated call stack depth, typically caused by infinite recursion without a termination base case. OutOfMemoryError (OOM) occurs when the JVM Heap or Metaspace has no more memory available to allocate a new object and the Garbage Collector cannot reclaim any more space.

Explore Related Java Tutorials

Accelerate your Java mastery by studying these tightly connected core tutorials:

Garbage Collection Guide →

How objects are moved across Young and Old generations.

JVM Internals →

ClassLoader and Execution Engine architecture.

Java Methods & Recursion →

Understanding method call frames on the stack.

Java Exceptions & Errors →

Handling OutOfMemoryError and StackOverflowError.

Share Share on Facebook Share on Twitter Share on LinkedIn Pin on Pinterest Share on Stumbleupon Share on Tumblr Share on Reddit Share on Diggit

You may also like this!