Java Stack vs. Heap: A Practical Memory Allocation Guide
What is Stack Memory?
In Java, the stack is a contiguous memory region that holds method call frames, local variables, and reference variables. Each time a method is invoked, a new frame is pushed onto the stack. Stack memory is organized in a Last‑In, First‑Out (LIFO) fashion, meaning the most recent call is the first to be removed when the method returns.
What is Heap Memory?
The heap is the area where Java objects are created. It holds all instances of classes and, optionally, reference variables that point to those objects. Unlike the stack, the heap is not tied to the lifetime of a single method call, allowing objects to persist beyond the scope in which they were created.
Memory Allocation in Java
Memory allocation in Java refers to the process of reserving virtual memory for program data. Variables are not allocated at declaration; instead, a reference is created. When the new operator is used, the JVM allocates space on the heap for the object.
Java divides memory into four distinct sections for efficient management:
- Heap
- Stack
- Code (Bytecode)
- Static (Class data)
- The Code section contains the compiled bytecode.
- The Stack holds method frames, local variables, and reference variables.
- The Heap stores objects and, occasionally, reference variables.
- The Static section keeps static fields and methods.
Local vs. Instance Variables
Instance variables are declared within a class but outside any method:
class Student {
int num; // instance variable
public void showData() {}
}
Local variables are declared inside a method (or as method parameters):
public void sum(int a) {
int x = a + 3; // a and x are local variables
}
Stack vs. Heap in Action
Let’s walk through an example to illustrate how the stack and heap interact during method calls.
Assume the main method calls m1():
public void m1() {
int x = 20;
}
A frame for m1 is pushed onto the stack. The local variable x is stored in this frame.
When m1 calls m2(int b), a new frame is added on top of m1's frame:
public void m2(int b) {
boolean c;
}
Similarly, m2 calls m3(), creating another frame:
Inside m3, an Account object is instantiated on the heap, while its reference ref lives on the stack:
class Account {
int p;
int q;
}
public void m3() {
Account ref = new Account();
// additional code
}
The reference variable ref is created in m3's stack frame and points to the heap‑allocated Account object.
When m3 completes, its stack frame is popped, leaving the Account object without any references. It becomes eligible for garbage collection.
The same process repeats for m2 and m1, ultimately returning control to main.
Object References as Instance Variables
Consider a class A that holds a reference to an instance of B:
public static void main(String[] args) {
A parent = new A();
// additional code
}
class A {
B child = new B();
int e;
}
class B {
int c;
int d;
}
Here, the child reference is stored on the heap as part of the A object, pointing to its own B instance.
Key Takeaways
- Each method call creates a new frame on the stack.
- Local variables reside on the stack; instance variables are on the heap.
- Reference variables are stored on the stack but point to heap objects.
- When a stack frame is popped, its local and reference variables are discarded, and any orphaned heap objects become eligible for garbage collection.
Java
- Java Variables and Literals: A Comprehensive Guide
- Java Methods: How to Define, Call, and Use Them Effectively
- Java Recursion: Understanding, Examples, and Trade‑Offs
- Mastering Method Overriding in Java
- Mastering Java Polymorphism: Concepts, Examples, and Best Practices
- Java Annotation Types: A Comprehensive Guide to Predefined, Custom, and Meta Annotations
- Java Stack Class – Comprehensive Guide to Usage and Methods
- Mastering Java's Iterator Interface: Practical Guide with Code Example
- Mastering Java String Replacement: replace(), replaceAll(), and replaceFirst() Explained
- Java 10 Enhancements: Allocate Heap on NV‑DIMM with -XX:AllocateHeapAt