⚡ Play a quiz
HomeLearnLanguagesJava › JVM memory model
Lesson 1 of 2 · Java

JVM Memory: Stack, Heap and Garbage Collection

Java50%

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.

Read time
8 min
Track
Languages
Sections
4
Practice
0

Stack and heap

The two regions
StackHeap
HoldsLocal variables, parameters, referencesAll objects and arrays
LifetimeThe method call's frameUntil no reference remains
Per threadOne stack eachShared by all threads
Managed byAutomatic push and popThe garbage collector
ExhaustionStackOverflowErrorOutOfMemoryError
Where each thing lives
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.

STACK — one per thread, unwinds on return HEAP — shared by all threads example() frame int count = 5 value lives here String name int[] scores frame popped → names vanish "Aditi" int[100] unreachable → collected reference Stack exhausted → StackOverflowError (runaway recursion) Heap exhausted → OutOfMemoryError (objects still reachable) A leak in Java is an object you forgot but something still points at.
The stack holds the names, the heap holds the objects. That split is why locals are thread-safe for free and why objects need a collector.

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.

Mutation vs reassignment
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"
tip

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.

Common mistake

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.

warn

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

Three words that share four letters and nothing else
KeywordWhat it isWhat it does
finalA modifierVariable cannot be reassigned, method cannot be overridden, class cannot be extended
finallyA blockRuns after try/catch whether or not an exception was thrown
finalize()A method on ObjectWas 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

More in Java