⚙️ JVM Internals & Memory · Advanced

Stack vs heap in Java

Frames and locals on the stack, objects on the heap; escape analysis.

🧩 The mysteryA method changes an array's first element, and the caller sees it. The same method then assigns a brand-new array, and the caller sees nothing. Same variable, two outcomes. Why?

Notepad and warehouse

Local variables (primitives *and* references) live in the current method's stack frame and disappear when it returns. Objects and arrays live on the heap and stay as long as something references them. The stack is a notepad you tear off; the heap is a warehouse.

A reference is a ticket

In String s = new String("hi"), only the reference s is in the frame; the String object is on the heap. The same goes for arrays: they are objects too.

String s = new String("hi");
// s: in the stack frame
// the String object: on the heap

Java copies values

Java always passes copies. For a primitive, that's a copy of the number. For an object, it's a copy of the reference: a second ticket to the same object. Changes made *through* the ticket are shared; pointing the copy at a new object changes only the method's own copy.

🔮 Predict it

Two tickets, one object

What does this print?

void grow(StringBuilder sb, int n) {
    sb.append("!"); n++;
    sb = new StringBuilder("new");
}
void main() {
    var sb = new StringBuilder("hi");
    int n = 1;
    grow(sb, n);
    System.out.println(sb + " " + n);
}
  1. hi! 1
  2. new 2
  3. hi 1
  4. new 1
Show the answer

sb.append("!") changes the shared object on the heap. n++ and sb = new StringBuilder(...) only change grow's own copies in its frame, which vanish when it returns.

⚠️ The trap

"It's local, so it's on the stack"

A tempting myth: a big array in a local variable lives on the stack and won't bother the GC. Wrong: only the reference is local. The ~200 MB array is on the heap, and can even cause OutOfMemoryError: Java heap space.

void load() {
    int[] buf = new int[50_000_000];
    // buf: tiny reference in the frame
    // the 200 MB array: on the heap
}
🤔 Think first

Can the JIT skip the heap?

An object is created inside a method and never stored or returned anywhere. Must it really be allocated on the heap?

Think about it, then reveal the answer

Not necessarily. HotSpot's escape analysis can prove it never leaves the method, and C2 can scalar-replace it: its fields become plain local values, so no heap allocation happens. Conceptually it's still a heap object; the JIT just optimizes the allocation away.

💼 In the real world

The interview classic

"Is Java pass-by-reference?" The precise answer: Java is pass-by-value, and for objects the value is a reference. It also explains why purely local data needs no synchronization: each thread runs with its own stack, so locals are private to the thread.

Key takeaways

  1. Locals and parameters live in the stack frame
  2. Objects and arrays live on the heap
  3. Arguments are copies: references are copied, objects are not
  4. Escape analysis can scalar-replace non-escaping objects

💡 The stack is a notepad you tear off when a task ends; the heap is a warehouse where items stay as long as someone holds their ticket.

🤯 Did you know?

Java has no keyword to allocate an object on the stack. Any stack-like allocation you get is an invisible JIT optimization (scalar replacement), never something you write in source code.

Practice questions

What does this print?

void reset(int[] arr, int n) {
    arr[0] = 0;
    n = 0;
    arr = new int[]{99};
}
void main() {
    int[] a = {5}; int n = 5;
    reset(a, n);
    System.out.println(a[0] + " " + n);
}
  1. 99 5
  2. 0 5
  3. 5 5
  4. 0 0
Check your answer

0 5. arr[0] = 0 changes the shared array on the heap. n = 0 and arr = new int[]{99} only change reset's own copies in its frame.

Inside a method you write `String s = new String("hi");`. Where do things live?

  1. Both s and the String object are on the stack
  2. Both are on the heap
  3. s is in metaspace, the object is on the heap
  4. The reference s is in the stack frame; the String object is on the heap
Check your answer

The reference s is in the stack frame; the String object is on the heap. The variable is just a reference stored in the frame. The object it points to is allocated on the heap.

Objects stay on the heap while something references them. But who decides what counts as "something"? Next: GC roots.