Stack vs Heap Memory in Java
When a Java program runs, the JVM uses two main kinds of memory for your data. The stack is a small, fast area that each thread has for itself; it holds method calls and their local variables. The heap is a large area shared by all threads; it holds every object and array you create with new. Knowing what goes where explains how references work, why local variables are thread-safe, and what StackOverflowError and OutOfMemoryError mean. In this lesson, you will learn the difference between stack and heap memory.
What Goes Where
| Stored on the stack | Stored on the heap |
|---|---|
| A frame for each method call in progress | Every object created with new |
Local variables of primitive types (int marks = 91) | Arrays, strings, records, collections |
| Local reference variables (the reference itself) | The fields of those objects, primitives included |
| Method parameters and return addresses | Static fields (as part of their class's data) |
A local variable such as Student s lives on the stack, but it only holds a reference; the Student object it points to lives on the heap.
Example: The Stack Grows and Shrinks
Let's see an example that shows the stack frames of nested calls, and what happens when recursion never stops.
public class StackDemo {
static int depth = 0;
static void a() { b(); }
static void b() { c(); }
static void c() {
for (StackTraceElement e : Thread.currentThread().getStackTrace()) {
System.out.println(" frame: " + e.getMethodName());
if (e.getMethodName().equals("main")) {
break;
}
}
}
static void forever() {
depth++;
forever();
}
public static void main(String[] args) {
System.out.println("Frames while inside c():");
a();
try {
forever();
} catch (StackOverflowError e) {
System.out.println("StackOverflowError after more than 1000 calls? "
+ (depth > 1000));
}
}
}Frames while inside c():
frame: getStackTrace
frame: c
frame: b
frame: a
frame: main
StackOverflowError after more than 1000 calls? trueExplanation: Each call pushes a new frame on the stack, so inside c() the stack holds frames for main, a, b and c (and getStackTrace itself). The loop stops at main; below it are a few frames of the launcher that started the program. When a method returns, its frame is popped. forever() keeps calling itself, so frames pile up until the thread's stack (about 1 MB by default on 64-bit systems) is full, and the JVM throws StackOverflowError. The exact depth depends on the frame size and the stack size.
Differences Between Stack and Heap
| Stack | Heap | |
|---|---|---|
| Holds | Method frames, local variables, references | Objects and arrays |
| Owner | One per thread | Shared by all threads |
| Size | Small (about 1 MB per thread by default, -Xss) | Large (set with -Xms and -Xmx) |
| Allocation | Automatic, very fast (move a pointer) | new, fast but managed |
| Freed | When the method returns | By the garbage collector, when unreachable |
| Thread safety | Local variables are private to their thread | Objects can be shared and need care |
| Error when full | StackOverflowError | OutOfMemoryError: Java heap space |
Common Mistakes
Saying "objects are stored in variables". Variables hold references; objects are on the heap.
Thinking primitives are always on the stack. A primitive field of an object lives on the heap inside that object.
Fixing StackOverflowError by raising -Xss. Usually the real bug is recursion without a proper base case.
Interview Corner
What is the difference between stack and heap memory in Java?
Each thread has its own stack, which stores a frame per active method call with its local variables, parameters and references; frames are created and removed automatically as methods are called and return, and running out causes StackOverflowError. The heap is shared by all threads and stores all objects and arrays, including their fields; memory is reclaimed by the garbage collector once objects are unreachable, and running out causes OutOfMemoryError. A local reference variable lives on the stack while the object it refers to lives on the heap. (The JIT may also avoid heap allocation for objects that never escape a method, using escape analysis.)
Practice
- Easy: Write a recursive
factorialwith a missing base case, run it, and catch the error. - Harder: Run
StackDemowithjava -Xss256k StackDemo.javaand then-Xss4m, printingdepth. Explain the difference.
Key Takeaways
Was this tutorial helpful?