JVM Memory: Stack, Heap and Garbage Collection
Every Java memory question — why a StackOverflowError differs from an OutOfMemoryError, why modifying an object inside a method is visible to the caller but reassigning it is not — has the same root: the JVM keeps two very different regions of memory, and variables live in one while objects live in the other.
Stack and heap
| Stack | Heap | |
|---|---|---|
| Holds | Local variables, parameters, references | All objects and arrays |
| Lifetime | The method call's frame | Until no reference remains |
| Per thread | One stack each | Shared by all threads |
| Managed by | Automatic push and pop | The garbage collector |
| Exhaustion | StackOverflowError | OutOfMemoryError |
void example() {
int count = 5; // the value 5 is on the stack
String name = "Aditi"; // reference on the stack, object on the heap
int[] scores = new int[100]; // reference on the stack, array on the heap
} // frame popped: count and the references vanish,
// the heap objects survive until unreachable
Because each thread gets its own stack, locals are automatically thread-safe. The heap is shared, which is precisely why concurrent access to objects needs synchronisation.
Java is always pass-by-value
This is the most frequently mis-answered Java question. Java passes copies — always. For an object, the thing copied is the reference, so both names point at the same heap object. That makes mutation visible to the caller and reassignment invisible.
void mutate(StringBuilder sb) {
sb.append(" world"); // same object -> caller sees "hello world"
}
void reassign(StringBuilder sb) {
sb = new StringBuilder("bye"); // only the local copy now points elsewhere
} // caller still sees "hello"
How to phrase it
"Java is pass-by-value. For objects, the reference is passed by value — so I can change the object's state, but I cannot make the caller's variable point at a different object." That sentence answers the question and the follow-up at once.
Garbage collection
An object becomes eligible for collection when it is no longer reachable from any live root — a running thread's stack, a static field, or a JNI reference. Reachability, not reference counting: two objects pointing at each other with nothing else pointing at them are both garbage, so cycles are collected correctly.
The heap is split by age, because most objects die young. New objects are allocated in the young generation, where collection is fast and frequent; survivors are promoted to the old generation, which is collected less often and more expensively.
Calling System.gc()
It is a suggestion, not a command, and the JVM may ignore it entirely. Calling it in application code usually makes things worse by forcing an expensive full collection at a moment the collector had good reason to avoid.
A memory leak in a garbage-collected language
Garbage collection removes unreachable objects. An object you forgot about but that is still referenced — a growing static Map, a listener never unregistered, a cache with no eviction — is reachable, so it is kept forever. That is the standard Java leak.
final, finally, finalize
| Keyword | What it is | What it does |
|---|---|---|
final | A modifier | Variable cannot be reassigned, method cannot be overridden, class cannot be extended |
finally | A block | Runs after try/catch whether or not an exception was thrown |
finalize() | A method on Object | Was called before collection; deprecated since Java 9 — use try-with-resources |
Note that final on a reference freezes the reference, not the object: a final List can still have elements added. Immutability of the object needs List.copyOf() or an unmodifiable wrapper.
Key takeaways
- Locals and references live on the per-thread stack; all objects live on the shared heap.
- Java is pass-by-value; for objects the reference is copied, so mutation is visible but reassignment is not.
- Garbage collection removes unreachable objects, so cycles are fine but forgotten references leak.
- final freezes a reference, not the object it points at.
Frequently asked questions
Is Java pass-by-value or pass-by-reference?
Always pass-by-value. For objects, the value copied is the reference, so a method can change the object's state but cannot make the caller's variable refer to a different object.
What is the difference between stack and heap memory in Java?
The stack holds local variables and references per thread and unwinds automatically when a method returns. The heap holds all objects, is shared across threads, and is reclaimed by the garbage collector.
Can a Java program leak memory?
Yes. The collector only removes unreachable objects, so anything still referenced — a static collection that keeps growing, an unregistered listener, an unbounded cache — is retained indefinitely.
Test yourself on Java
Reading is not recall. Take a timed quiz on this topic solo, or share a room code and battle friends on it.
⚡ Start the Java quiz