Understanding Java Memory: A Deep Dive into Stack, Heap, and Metaspace
If you’ve ever wondered where Java stores your variables, objects, and method calls, you’re not alone. Understanding Java’s memory model —…

Understanding Java Memory: A Deep Dive into Stack, Heap, and Metaspace
If you’ve ever wondered where Java stores your variables, objects, and method calls, you’re not alone. Understanding Java’s memory model — specifically the Stack, Heap, and Metaspace — is crucial for writing efficient, bug-free code and troubleshooting memory issues like StackOverflowError or OutOfMemoryError.
In this comprehensive guide, we’ll explore:
- What Stack, Heap, and Metaspace memory are
- What gets stored in each memory area
- How static variables and methods are stored
- How they work with real-world examples
- Best practices for modern Java development
Table of Contents
- Java Memory Model Overview
- Stack Memory: The Fast Lane
- Heap Memory: The Storage Warehouse
- Metaspace: The Class Metadata Repository
- Static Variables and Methods: Where Do They Live?
- How Stack, Heap, and Metaspace Work Together
- Real-World Examples with Method Calls
- Common Memory Errors and How to Avoid Them
1. Java Memory Model Overview
When a Java application runs, the JVM (Java Virtual Machine) divides memory into several areas. The three most important for developers are:
- Stack Memory: Fast, thread-specific storage for method execution
- Heap Memory: Shared storage for all objects created during runtime
- Metaspace: Native memory area for class metadata (introduced in Java 8)
Think of it like this:
- Stack = Your desk workspace (small, organized, fast access)
- Heap = Your warehouse (large, shared, needs management)
- Metaspace = Your blueprint library (class definitions, static data, method code)
┌─────────────────────────────────────────────────────┐
│ JVM MEMORY STRUCTURE │
├─────────────────────────────────────────────────────┤
│ STACK (Thread-specific) │
│ - Method frames │
│ - Local variables (primitives) │
│ - References to objects │
├─────────────────────────────────────────────────────┤
│ HEAP (Shared by all threads) │
│ - All objects (created with new) │
│ - Instance variables │
│ - Arrays │
├─────────────────────────────────────────────────────┤
│ METASPACE (Native Memory) │
│ - Class metadata │
│ - Static variables (references/primitives) │
│ - Static methods (bytecode) │
│ - Constant pool │
└─────────────────────────────────────────────────────┘
2. Stack Memory: The Fast Lane
What is Stack Memory?
Stack memory is a special region where Java stores:
- Local variables (primitives like
int,boolean,char) - Method call information (method parameters, return addresses)
- References to objects (not the actual objects!)
Key Characteristics
┌───────────────────────┬─────────────────────────────────────────────────────────────────┐
│ FEATURE │ DESCRIPTION │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Size │ Small and fixed (typically 512KB - 1MB per thread) │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Access │ LIFO (Last In, First Out) - like a stack of plates │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Speed │ Very fast allocation and deallocation │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Thread Safety │ Each thread has its own stack │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Lifetime │ Variables exist only while the method is executing │
├───────────────────────┼─────────────────────────────────────────────────────────────────┤
│ Management │ Automatically managed - no garbage collection needed │
└───────────────────────┴─────────────────────────────────────────────────────────────────┘
What Gets Stored in Stack?
- Primitive Data Types
int age = 25; // Stored in stack
double salary = 50000.0; // Stored in stack
boolean isActive = true; // Stored in stack
char grade = 'A'; // Stored in stack
- Object References (but not the actual object)
String name = "John"; // Reference stored in stack, object in heap
Person person = new Person(); // Reference in stack, Person object in heap
- Method Call Frames
- Local variables
- Method parameters
- Return address
Speaking of Method Frames lets dive into how methods behave inside stack:
How Methods Behave in Stack Memory
Understanding how methods are executed and stored in the stack is crucial for grasping Java’s memory model. Let’s explore this in detail.
The Stack Frame Concept
Every time a method is called, the JVM creates a stack frame (also called an activation record) and pushes it onto the thread’s stack. This frame contains everything needed to execute that method.
What’s Inside a Stack Frame?
═══════════════════════════════════════════════════════════════════════
STACK FRAME CONTENTS
═══════════════════════════════════════════════════════════════════════
COMPONENT │ DESCRIPTION
───────────────────────┼───────────────────────────────────────────────
Local Variable Array │ Holds method parameters and local variables
───────────────────────┼───────────────────────────────────────────────
Operand Stack │ Workspace for computations and operations
───────────────────────┼───────────────────────────────────────────────
Frame Data │ Return address, exception handling info
───────────────────────┼───────────────────────────────────────────────
Return Value │ Space to store method's return value
═══════════════════════════════════════════════════════════════════════
Method Execution Flow: Step-by-Step
Let’s see how the stack behaves with a practical example:
public class Calculator {
public static void main(String[] args) {
int a = 5;
int b = 10;
int result = add(a, b);
System.out.println(result);
}
public static int add(int x, int y) {
int sum = x + y;
return sum;
}
}
Step 1: main() Method Starts
When main() is called, a stack frame is created:
STACK (Thread: main)
┌─────────────────────────────────┐
│ main() Stack Frame │
│ ┌───────────────────────────┐ │
│ │ Local Variables: │ │
│ │ - args = reference │ │
│ │ - a = 5 │ │
│ │ - b = 10 │ │
│ │ - result = ? (undefined) │ │
│ └───────────────────────────┘ │
│ Return Address: (JVM entry) │
└─────────────────────────────────┘
Step 2: add() Method Called
When add(a, b) is invoked, a NEW stack frame is pushed on top:
STACK (Thread: main)
┌─────────────────────────────────┐ ← TOP (Current)
│ add() Stack Frame │
│ ┌───────────────────────────┐ │
│ │ Parameters: │ │
│ │ - x = 5 (copy of a) │ │
│ │ - y = 10 (copy of b) │ │
│ │ Local Variables: │ │
│ │ - sum = ? (undefined) │ │
│ └───────────────────────────┘ │
│ Return Address: main() line 5 │
├─────────────────────────────────┤
│ main() Stack Frame │
│ ┌───────────────────────────┐ │
│ │ Local Variables: │ │
│ │ - args = reference │ │
│ │ - a = 5 │ │
│ │ - b = 10 │ │
│ │ - result = ? (waiting) │ │
│ └───────────────────────────┘ │
│ Return Address: (JVM entry) │
└─────────────────────────────────┘
Important Notes:
✅ Parameters x and y are copies of a and b (pass by value)
✅ main() is still on the stack but paused (waiting for add() to return)
✅ The stack grows upward (newer frames on top)
Step 3: add() Executes
Inside add(), the computation happens:
STACK (Thread: main)
┌─────────────────────────────────┐
│ add() Stack Frame │
│ ┌───────────────────────────┐ │
│ │ Parameters: │ │
│ │ - x = 5 │ │
│ │ - y = 10 │ │
│ │ Local Variables: │ │
│ │ - sum = 15 ✓ (computed) │ │
│ └───────────────────────────┘ │
│ Return Value: 15 │
│ Return Address: main() line 5 │
├─────────────────────────────────┤
│ main() Stack Frame (paused) │
│ ... │
└─────────────────────────────────┘
Step 4: add() Returns
When add() finishes:
- The return value (
15) is placed in a return slot - The
add()stack frame is popped (removed) - Control returns to
main() - The return value is assigned to
result
STACK (Thread: main)
┌─────────────────────────────────┐
│ main() Stack Frame │
│ ┌───────────────────────────┐ │
│ │ Local Variables: │ │
│ │ - args = reference │ │
│ │ - a = 5 │ │
│ │ - b = 10 │ │
│ │ - result = 15 ✓ │ │
│ └───────────────────────────┘ │
│ Return Address: (JVM entry) │
└─────────────────────────────────┘
(add() frame is completely removed)
Step 5: main() Completes
When main() finishes, its frame is also popped:
STACK (Thread: main)
┌─────────────────────────────────┐
│ (empty - thread terminates) │
└─────────────────────────────────┘
Key Behaviors of Methods in Stack
1. LIFO (Last In, First Out) Principle
Method Call Sequence: Stack Growth:
main() → ┌─────────┐
└→ methodA() → │ methodC │ ← TOP (most recent)
└→ methodB() → ├─────────┤
└→ methodC() │ methodB │
├─────────┤
│ methodA │
├─────────┤
│ main() │ ← BOTTOM (oldest)
└─────────┘
Return Sequence: Stack Shrinkage:
methodC returns → ┌─────────┐
methodB returns → │ methodA │ ← methodC popped
methodA returns → ├─────────┤
main() returns │ main() │ ← methodB popped
└─────────┘
2. Pass-by-Value for Primitives
public class PassByValueDemo {
public static void main(String[] args) {
int original = 100;
modify(original);
System.out.println(original); // Still 100!
}
public static void modify(int value) {
value = 999; // Only changes the COPY
}
}
---------------------------------------------------------------
STACK
┌──────────────────────────┐
│ modify() frame │
│ - value = 999 │ ← Changed copy
├──────────────────────────┤
│ main() frame │
│ - original = 100 │ ← Original unchanged
└──────────────────────────┘
3. Pass-by-Value for Object References
public class ReferenceDemo {
public static void main(String[] args) {
Person person = new Person("Alice");
changeName(person);
System.out.println(person.name); // "Bob" - changed!
}
public static void changeName(Person p) {
p.name = "Bob"; // Modifies the SAME object
}
}
---------------------------------------------------------
STACK HEAP
┌──────────────────────┐ ┌─────────────────┐
│ changeName() frame │ │ Person @0x100 │
│ - p = 0x100 ────────┼───────▶ name = "Bob" │
├──────────────────────┤ └─────────────────┘
│ main() frame │ ▲
│ - person = 0x100 ───┼──────────────┘
└──────────────────────┘
Key Point: The reference is copied (both point to same object), but the object itself lives in the heap.
4. Recursion and Stack Depth
Recursive methods create multiple stack frames:
public static int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// Call: factorial(4)
-----------------------------------------------------------------------
┌───────────────────────┐
│ factorial(4) │ ← Current call
│ - n = 4 │
│ - waiting for result │
├───────────────────────┤
│ factorial(3) │
│ - n = 3 │
├───────────────────────┤
│ factorial(2) │
│ - n = 2 │
├───────────────────────┤
│ factorial(1) │ ← Base case (returns 1)
│ - n = 1 │
│ - returns 1 ✓ │
├───────────────────────┤
│ main() │
└───────────────────────┘
Stack Unwinding (Returning):
Step 1: factorial(1) returns 1
Step 2: factorial(2) returns 2 * 1 = 2
Step 3: factorial(3) returns 3 * 2 = 6
Step 4: factorial(4) returns 4 * 6 = 24
Danger: Too many recursive calls → StackOverflowError
// BAD: Infinite recursion
public static void infiniteRecursion() {
infiniteRecursion(); // No base case!
}
// Stack keeps growing until:
// Exception in thread "main" java.lang.StackOverflowError
3. Heap Memory: The Storage Warehouse
What is Heap Memory?
Heap memory is where all Java objects and instance variables live. It’s shared across all threads in your application.
╔═══════════════════════╦═════════════════════════════════════════════════════════════════╗
║ FEATURE ║ DESCRIPTION ║
╠═══════════════════════╬═════════════════════════════════════════════════════════════════╣
║ Size ║ Large and configurable (can scale to many GBs) ║
╠═══════════════════════╬═════════════════════════════════════════════════════════════════╣
║ Access ║ Random Access; objects accessible from anywhere ║
╠═══════════════════════╬═════════════════════════════════════════════════════════════════╣
║ Speed ║ Slower (overhead for allocation and fragmentation) ║
╠═══════════════════════╬═════════════════════════════════════════════════════════════════╣
║ Thread Safety ║ Shared; requires synchronization (locks/volatile) ║
╠═══════════════════════╬═════════════════════════════════════════════════════════════════╣
║ Lifetime ║ Global; objects persist until no longer referenced ║
╠═══════════════════════╬═════════════════════════════════════════════════════════════════╣
║ Management ║ Managed by the Garbage Collector (GC) ║
╚═══════════════════════╩═════════════════════════════════════════════════════════════════╝
Heap Structure (Generational)
The heap is divided into generations to optimize garbage collection:
┌─────────────────────────────────────────────────────────────────┐
│ HEAP MEMORY │
├─────────────────────────────────────────────────────────────────┤
│ YOUNG GENERATION │ OLD GENERATION │
├──────────┬──────────┬──────────┤ (TENURED) │
│ │ Survivor │ Survivor │ │
│ Eden │ S0 │ S1 │ Long-lived Objects │
│ │ │ │ │
└──────────┴──────────┴──────────┴────────────────────────────────┘
What Gets Stored in Heap?
- All Objects Created with
new
String name = new String("John"); // String object in heap
Person person = new Person(); // Person object in heap
int[] numbers = new int[10]; // Array object in heap
2.Instance Variables (Object Fields)
public class Person {
private String name; // Stored in heap (with Person object)
private int age; // Stored in heap (with Person object)
private Address address; // Reference in heap, Address object also in heap
}
3.String Pool (Special area in heap)
String s1 = "Hello"; // "Hello" stored in String pool (heap)
String s2 = "Hello"; // Reuses same "Hello" from pool
4. Metaspace: The Class Metadata Repository
What is Metaspace?
Metaspace is a native memory region introduced in Java 8 that replaced the older PermGen (Permanent Generation). It stores class-level metadata and structural information about your application.
┌─────────────────────────────────────────────────────────────────┐
│ NATIVE MEMORY │
├─────────────────────────────────────────────────────────────────┤
│ METASPACE │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ CLASS METADATA STORAGE │ │
│ │ - Method Data - Annotations - Constant Pool │ │
│ │ - Bytecode - Field Data - vTable │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
What Gets Stored in Metaspace?
- Class Metadata
- Class structure and definitions
- Field descriptions
- Method signatures
- Method Bytecode
- Compiled bytecode for all methods (static and instance)
- Method metadata
3.Static Variables
- Primitive static values
- References to static objects (actual objects in heap)
4.Constant Pool
- Runtime constant pool per class
- Symbolic references
5.JIT Compiled Code (partially)
- Some optimized native code
JAVA 7 (PermGen) JAVA 8+ (Metaspace)
┌────────────────┐ ┌────────────────┐
│ Fixed Size │ │ Dynamic Size │
│ Part of Heap │ ──────▶ │ Native Memory │
│ GC'd with Heap │ │ Auto-grows │
│ Size: -XX: │ │ Size: -XX:Max │
│ MaxPermSize │ │ MetaspaceSize │
└────────────────┘ └────────────────┘
Benefits of Metaspace:
- No more
OutOfMemoryError: PermGen space - Automatic resizing based on application needs
- Better memory utilization
- Only limited by available native memory
5. Static Variables and Methods: Where Do They Live?
Static Methods Storage
Static methods are stored in Metaspace as part of the class metadata:
public class MathUtils {
public static int add(int a, int b) {
return a + b;
}
}
Memory Layout:
METASPACE
┌─────────────────────────────────┐
│ Class: MathUtils │
│ ┌─────────────────────────────┐ │
│ │ Method: add(int, int) │ │
│ │ - Bytecode │ │
│ │ - Method signature │ │
│ │ - Access modifiers │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────┘
Key Points:
- Static methods belong to the class, not instances
- Bytecode and metadata stored in Metaspace
- No object needed to call them
- Loaded once when class is loaded
Static Variables Storage
Static variables have a more nuanced storage model:
Case 1: Static Primitive Variables
public class Counter {
public static int count = 0; // Primitive
}
------------------------------------------------------------
METASPACE
┌─────────────────────────────────┐
│ Class: Counter │
│ ┌─────────────────────────────┐ │
│ │ static int count = 0 │ │
│ │ (value stored here) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────┘
Storage: The value itself is stored in Metaspace.
Case 2: Static Object References
public class Database {
public static Connection connection = new Connection();
}
-----------------------------------------------------------
METASPACE HEAP
┌───────────────────────┐ ┌─────────────────────────┐
│ Class: Database │ │ │
│ ┌───────────────────┐ │ │ Connection Object │
│ │ static Connection │ │ │ @0x500 │
│ │ connection=0x500 │─┼──────▶┌─────────────────────┐ │
│ └───────────────────┘ │ │ │ host = "localhost" │ │
└───────────────────────┘ │ │ port = 5432 │ │
│ └─────────────────────┘ │
└─────────────────────────┘
Storage:
- The reference (pointer
0x500) is stored in Metaspace - The actual object is stored in Heap
Complete Example with Static Members:
public class Config {
// Static primitive - value in Metaspace
public static int MAX_CONNECTIONS = 100;
// Static object reference - reference in Metaspace, object in Heap
public static String APP_NAME = new String("MyApp");
// Static array reference - reference in Metaspace, array in Heap
public static int[] PORTS = {8080, 8081, 8082};
// Static method - bytecode in Metaspace
public static void configure() {
System.out.println("Configuring...");
}
// Instance variable - stored in Heap with object
private String environment;
// Instance method - bytecode in Metaspace (shared by all instances)
public void deploy() {
System.out.println("Deploying...");
}
}
--------------------------------------------------------------------------
METASPACE HEAP
┌────────────────────────────────────┐ ┌──────────────────────────┐
│ Class: Config │ │ │
│ ┌────────────────────────────────┐ │ │ String Object @0x100 │
│ │ static int MAX_CONNECTIONS=100│ │ │ "MyApp" │
│ │ static String APP_NAME = 0x100│─┼──────▶ │
│ │ static int[] PORTS = 0x200 │─┼────┐ │ │
│ │ │ │ │ │ int[] Array @0x200 │
│ │ static void configure() { │ │ └─▶[8080, 8081, 8082] │
│ │ // bytecode │ │ │ │
│ │ } │ │ │ Config Object @0x300 │
│ │ │ │ │ ┌──────────────────────┐ │
│ │ void deploy() { │ │ │ │environment="prod" │ │
│ │ // bytecode (shared) │ │ │ └──────────────────────┘ │
│ │ } │ │ └──────────────────────────┘
│ └────────────────────────────────┘ │
└────────────────────────────────────┘
┌─────────────────────────────┬──────────────────────────────────┬─────────────────────────────┐
│ ITEM │ REFERENCE/VALUE LOCATION │ OBJECT LOCATION │
├─────────────────────────────┼──────────────────────────────────┼─────────────────────────────┤
│ Static primitive │ Metaspace (value) │ N/A │
├─────────────────────────────┼──────────────────────────────────┼─────────────────────────────┤
│ Static object reference │ Metaspace (reference) │ Heap (object) │
├─────────────────────────────┼──────────────────────────────────┼─────────────────────────────┤
│ Static method │ Metaspace (bytecode) │ N/A │
├─────────────────────────────┼──────────────────────────────────┼─────────────────────────────┤
│ Instance variable │ Heap (with object) │ Heap (if object) │
├─────────────────────────────┼──────────────────────────────────┼─────────────────────────────┤
│ Instance method │ Metaspace (bytecode, shared) │ N/A │
└─────────────────────────────┴──────────────────────────────────┴─────────────────────────────┘
6. How Stack, Heap, and Metaspace Work Together
Let’s see a complete example showing all three memory areas:
public class Application {
// Static variable
public static int instanceCount = 0;
// Instance variable
private String name;
// Constructor
public Application(String name) {
this.name = name;
instanceCount++;
}
// Static method
public static void printCount() {
System.out.println("Count: " + instanceCount);
}
// Instance method
public void display() {
String message = "Application: " + name;
System.out.println(message);
}
public static void main(String[] args) {
int localVar = 42;
Application app1 = new Application("App1");
Application app2 = new Application("App2");
app1.display();
printCount();
}
}
-------------------------------------------------------------------
STACK (main thread) METASPACE
┌────────────────────────┐ ┌──────────────────────────────────┐
│ main() frame │ │ Class: Application │
│ ┌────────────────────┐ │ │ ┌──────────────────────────────┐ │
│ │ localVar = 42 │ │ │ │ static int instanceCount = 2 │ │
│ │ app1 = 0x100 │─┼────┐ │ │ │ │
│ │ app2 = 0x200 │─┼──┐ │ │ │ static void printCount() { │ │
│ └────────────────────┘ │ │ │ │ │ // bytecode │ │
├────────────────────────┤ │ │ │ │ } │ │
│ display() frame │ │ │ │ │ │ │
│ ┌────────────────────┐ │ │ │ │ │ void display() { │ │
│ │ message = 0x300 │─┼─┐│ │ │ │ // bytecode │ │
│ └────────────────────┘ │ ││ │ │ │ } │ │
└────────────────────────┘ ││ │ │ └──────────────────────────────┘ │
││ │ └──────────────────────────────────┘
││ │
HEAP ││ │
┌──────────────────────────┼┼─┼────────────────────────┐
│ ││ │ │
│ Application @0x100 ◀─────┘│ │ │
│ ┌────────────────────────┐│ │ │
│ │ name = 0x400 ──────────┼┼─┼──┐ │
│ └────────────────────────┘│ │ │ │
│ │ │ │ │
│ Application @0x200 ◀───────┘ │ │ │
│ ┌────────────────────────┐ │ │ │
│ │ name = 0x500 ──────────┼───┼──┼──┐ │
│ └────────────────────────┘ │ │ │ │
│ │ │ │ │
│ String @0x300 ◀──────────────┘ │ │ │
│ "Application: App1" │ │ │
│ │ │ │
│ String @0x400 ◀─────────────────┘ │ │
│ "App1" │ │
│ │ │
│ String @0x500 ◀─────────────────────┘ │
│ "App2" │
└────────────────────────────────────────────────────────┘
7. Real-World Examples with Method Calls
Example 1: Recursive Method and Stack
public class FactorialDemo {
public static int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
public static void main(String[] args) {
int result = factorial(5);
System.out.println(result);
}
}
-------------------------------------------------------------
STACK (grows downward)
┌─────────────────────┐
│ factorial(5) frame │ ← Current
│ n = 5 │
├─────────────────────┤
│ factorial(4) frame │
│ n = 4 │
├─────────────────────┤
│ factorial(3) frame │
│ n = 3 │
├─────────────────────┤
│ factorial(2) frame │
│ n = 2 │
├─────────────────────┤
│ factorial(1) frame │
│ n = 1 │ ← Base case reached, starts unwinding
├─────────────────────┤
│ main() frame │
│ result = ? │
└─────────────────────┘
If the recursion goes too deep, you’ll get a StackOverflowError because the stack has a fixed size!
Example 2: Static Variables Across Instances
public class Employee {
private static int employeeCount = 0; // Static - in Metaspace
private String name; // Instance - in Heap
private int id; // Instance - in Heap
public Employee(String name) {
this.name = name;
this.id = ++employeeCount;
}
public static int getEmployeeCount() {
return employeeCount;
}
}
public class Main {
public static void main(String[] args) {
Employee emp1 = new Employee("Alice");
Employee emp2 = new Employee("Bob");
System.out.println(Employee.getEmployeeCount()); // 2
}
}
----------------------------------------------------------------------
METASPACE HEAP
┌────────────────────────────┐ ┌──────────────────────────┐
│ Class: Employee │ │ Employee @0x100 │
│ ┌────────────────────────┐ │ │ ┌──────────────────────┐ │
│ │ static int │ │ │ │ name = 0x300 ──────┐ │ │
│ │ employeeCount = 2 │ │ │ │ id = 1 │ │ │
│ │ │ │ │ └────────────────────┼─┘ │
│ │ static int │ │ │ │ │
│ │ getEmployeeCount() { │ │ │ Employee @0x200 │ │
│ │ return employeeCount;│ │ │ ┌──────────────────┼───┐ │
│ │ } │ │ │ │ name = 0x400 ──┐ │ │ │
│ └────────────────────────┘ │ │ │ id = 2 │ │ │ │
└────────────────────────────┘ │ └────────────────┼─┼───┘ │
│ │ │ │
│ String @0x300 ◀──┘ │ │
│ "Alice" │ │
│ │ │
│ String @0x400 ◀────┘ │
│ "Bob" │
└──────────────────────────┘
Notice: Both emp1 and emp2 share the same static variable employeeCount stored in Metaspace!
8. Common Memory Errors and How to Avoid Them
StackOverflowError
Cause: Too many method calls (usually from infinite recursion)
// BAD: Infinite recursion
public void recursiveMethod() {
recursiveMethod(); // No base case!
}
Solution: Always have a base case in recursion or use iteration instead.
OutOfMemoryError: Java heap space
Cause: Creating too many objects or memory leaks
// BAD: Memory leak
public class LeakyCache {
private static Map<String, byte[]> cache = new HashMap<>();
public void addToCache(String key) {
cache.put(key, new byte[1024 * 1024]); // 1MB
// Never removed!
}
}
Solution: Use appropriate data structures, null out references, use WeakHashMap for caches.
OutOfMemoryError: Metaspace
Cause: Too many classes loaded (common in applications with dynamic class loading)
// BAD: Classloader leak
public void loadClasses() {
while (true) {
URLClassLoader loader = new URLClassLoader(urls);
Class<?> clazz = loader.loadClass("com.example.MyClass");
// Loader never released!
}
}
Solution:
# Increase Metaspace size
java -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m MyApp
# Monitor Metaspace
java -XX:+PrintGCDetails -XX:+PrintMetaspaceSize MyApp
Here is a comprehensive table overview of the topics we cleared:
╔═══════════════════╦═══════════════════════════════╦═══════════════════════════════╦═══════════════════════════════╗
║ ASPECT ║ STACK ║ HEAP ║ METASPACE ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ What's Stored ║ • Primitives ║ • All objects ║ • Class metadata ║
║ ║ • References ║ • Instance variables ║ • Static variables/methods ║
║ ║ • Method frames ║ • Arrays ║ • Constant pool ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ Location ║ JVM memory ║ JVM memory ║ Native memory ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ Size ║ Small ║ Large ║ Dynamic ║
║ ║ (512KB - 1MB per thread) ║ (GBs) ║ (configurable max) ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ Access Pattern ║ LIFO ║ Random access ║ ClassLoader access ║
║ ║ (Last In, First Out) ║ ║ ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ Speed ║ Very Fast ⚡⚡⚡ ║ Fast ⚡⚡ ║ Medium ⚡ ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ Thread Safety ║ Each thread has its own ║ Shared by all threads ║ Shared by all threads ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ Lifetime ║ Until method returns ║ Until garbage collected ║ Until class unloaded ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ Management ║ Automatic ║ Garbage Collector (GC) ║ Automatic (tunable) ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ Common Errors ║ StackOverflowError ║ OutOfMemoryError: ║ OutOfMemoryError: ║
║ ║ ║ Java heap space ║ Metaspace ║
╠═══════════════════╬═══════════════════════════════╬═══════════════════════════════╬═══════════════════════════════╣
║ Config Flags ║ -Xss ║ -Xms, -Xmx ║ -XX:MetaspaceSize ║
║ ║ ║ ║ -XX:MaxMetaspaceSize ║
╚═══════════════════╩═══════════════════════════════╩═══════════════════════════════╩═══════════════════════════════╝
Further Reading
- Permgen vs Metaspace in Java — Baeldung
- MetaSpace in Java 8 with Examples — GeeksforGeeks
- Metaspace setting and tuning — Red Hat Developer
- Understanding OutOfMemoryError in Java
About the Author
IT IS A PERSONAL(IF U SEE THIS ITS PROBABLY PUBLIC NOW) JAVA VAULT FOR INTERVIEW PREPARATION
메타데이터
- post_id
- ce339964733d
- slug
- understanding-java-memory-a-deep-dive-into-stack-heap-and-metaspace-ce339964733d
- url
- https://medium.com/@huseynliramazan192/understanding-java-memory-a-deep-dive-into-stack-heap-and-metaspace-ce339964733d
- canonical_url
- https://medium.com/@huseynliramazan192/understanding-java-memory-a-deep-dive-into-stack-heap-and-metaspace-ce339964733d
- author_url
- https://medium.com/@huseynliramazan192
- status
- ok
- fetched_at
- 2026-07-08 08:18:29