🧩 Methods · Beginner

StackOverflowError in Java

Missing base case or too-deep recursion exhausts the stack.

🧩 The mysteryYour recursive sum works perfectly for 1,000. For 10,000,000 it explodes, and there's no bug in the logic at all. What ran out?

A full stack

Each call adds a frame, and the call stack has a limited size. Recursion without a reachable base case, or recursion that's simply too deep, keeps pushing frames until the JVM throws **StackOverflowError**.

static int forever(int n) {
    return forever(n + 1); // no base case
}
// forever(0) -> StackOverflowError
šŸ”® Predict it

Ping-pong

Two methods call each other. What happens?

static int ping(int n) {
    return pong(n + 1);
}
static int pong(int n) {
    return ping(n + 1);
}
void main() {
    System.out.println(ping(0));
}
  1. Runs forever
  2. Throws StackOverflowError
  3. Compile error
Show the answer

StackOverflowError. Recursion doesn't have to be one method calling itself: ping calls pong calls ping... Each call pushes a frame, and none ever returns.

āš ļø The trap

The base case you skip over

A base case only helps if the recursion reaches it. fact(0) goes to -1, -2, -3... and never equals 1. Use n <= 1 so 0 (and anything smaller) stops too.

static long fact(int n) {
    if (n == 1) return 1; // 0 skips it
    return n * fact(n - 1);
}
// fix: if (n <= 1) return 1;

An Error, not an Exception

StackOverflowError extends VirtualMachineError, which extends **Error. It's unchecked**: no throws clause needed. Errors signal serious problems; it means fix the bug, not catch it and carry on.

Throwable
 ā”œā”€ Exception ...
 └─ Error
     └─ VirtualMachineError
         └─ StackOverflowError
šŸ¤” Think first

Loops vs calls

A while (true) loop that only increments a counter runs forever. Will it ever throw StackOverflowError?

Think about it, then reveal the answer

No. A loop reuses the same frame, so the stack never grows; it just runs forever. Only calls push frames. That's also why turning recursion into a loop cures overflow.

Too deep, not wrong

āœ— Recursive
static long sum(int n) {
    if (n == 0) return 0;
    return n + sum(n - 1);
}
// sum(10_000_000) -> overflow

Correct logic, but 10 million frames won't fit. Java doesn't optimize tail calls, so every call costs a frame.

āœ“ Loop
static long sum(int n) {
    long total = 0;
    for (int i = 1; i <= n; i++)
        total += i;
    return total;
}

Same answer in a single frame, at any size.

šŸ’¼ In the real world

In real projects

Recursive parsers can be attacked with absurdly deep input (JSON nested thousands of levels) to crash servers with StackOverflowError, so real parsers enforce depth limits. You can raise the stack size with java -Xss4m, but rewriting deep recursion as a loop is the robust fix.

Key takeaways

  1. Causes: missing or unreachable base case, or very deep recursion
  2. StackOverflowError extends Error (via VirtualMachineError), so it's unchecked
  3. Fix the base case, or rewrite very deep recursion as a loop
  4. Java doesn't optimize tail calls, so even 'simple' recursion uses a frame per call
🤯 Did you know?

Stack Overflow, the famous programming Q&A site launched in 2008, is named after exactly this error.

Practice questions

What does this print?

static int down(int n) {
    return down(n - 1);
}
void main() {
    System.out.println(down(5));
}
  1. 0
  2. Throws StackOverflowError
  3. Compile error
  4. Throws OutOfMemoryError
Check your answer

Throws StackOverflowError. There is no base case. Each call pushes another frame until the stack is full and the JVM throws StackOverflowError.

Three of these can cause StackOverflowError. Which one can't?

  1. A recursive method with no base case
  2. Two methods that call each other forever
  3. A while (true) loop that only increments a counter
  4. Correct recursion that goes 10 million calls deep
Check your answer

A while (true) loop that only increments a counter. A loop reuses the same frame, so it never grows the stack; it just runs forever. The others all keep pushing new frames.

Next: what makes two methods "the same" in Java's eyes? Hint: the return type doesn't count.