The call stack in Java
Each call pushes a frame; returning pops it.
A stack of plates
Every method call pushes a new frame onto the call stack: that call's parameters, locals and the place to return to. Only the top frame runs. When it returns, its frame is popped and the caller resumes exactly where it left off. Last called, first to finish (LIFO).
void main() { a(); } // [main]
static void a() { b(); } // [main, a]
static void b() { } // [main, a, b]Trace the stack
What prints?
static void a() {
System.out.println("a1"); b();
System.out.println("a2");
}
static void b() {
System.out.println("b1"); c();
System.out.println("b2");
}
static void c() { System.out.println("c"); }
void main() { a(); }a1 b1 c b2 a2a1 a2 b1 b2 ca1 b1 b2 c a2
Show the answer
a pauses at its call to b; b pauses at its call to c. c finishes and is popped, so b resumes and prints b2, then a resumes and prints a2. Callers wait, then pick up right after the call.
Nested calls
What prints?
static int f(int x) {
System.out.print("f" + x + " ");
return x + 10;
}
void main() {
System.out.println(f(f(1)));
}f1 f11 21f11 f1 2121 f1 f11
Show the answer
The inner f(1) must finish first: it prints f1 and returns 11. Then f(11) prints f11 and returns 21, which println prints last.
Reading a stack trace
A stack trace lists frames newest first, with main at the bottom. The top line is where the exception actually happened; each line below is the caller of the line above. Here divide threw, average called it, and main called average.
java.lang.ArithmeticException: / by zero
at Main.divide(Main.java:9)
at Main.average(Main.java:5)
at Main.main(Main.java:2)Unwinding
divide throws an exception and nobody catches it. What happens to the frames of average and main?
Think about it, then reveal the answer
They're popped one by one as Java looks for a catch. That's called unwinding the stack. If no frame catches it, the program dies and prints the stack trace you just read.
The stack has a ceiling
The call stack has a limited size. Calls that never stop (or go absurdly deep) keep pushing frames until it's full, and the JVM throws StackOverflowError. A plain loop doesn't do this: it reuses the same frame.
static void again() {
again(); // pushes forever
}
// -> StackOverflowErrorIn real projects
Reading stack traces is a daily debugging skill. Start at the top, then scan down for the first line from your own code (like at com.myshop.Cart...): that's usually where to look. Long traces from frameworks often hide the real cause further down, after "Caused by:".
Key takeaways
- Last called, first to finish (LIFO)
- Each frame has its own parameters and locals
- A stack trace lists frames newest first, with main at the bottom
- Too many nested calls overflow the stack
💡 Like a stack of plates: each new call puts a plate on top, and you can only take the top plate off.
Since Java 9, the StackWalker API lets a running program walk its own call stack, frame by frame, to find out who called it.
Practice questions
What does this print?
static void a() {
System.out.println("a start");
b();
System.out.println("a end");
}
static void b() { System.out.println("b"); }
void main() {
a();
}- a start a end b
- a start b a end
- b a start a end
Check your answer
a start b a end. a pauses at the call to b. b's frame runs and is popped, then a resumes right after the call and prints a end.
What does this print?
static int f(int x) {
System.out.print("f" + x + " ");
return x * 2;
}
void main() {
System.out.println(f(f(1)));
}- f2 f1 4
- f1 f2 4
- 4 f1 f2
- f1 4
Check your answer
f1 f2 4. The inner f(1) must finish first: it prints f1 and returns 2. Then f(2) prints f2 and returns 4, which println prints last.