← Debugging Guides

阅读中文版本

Java — StackOverflowError from runaway recursion

Find the unbounded recursion behind StackOverflowError and convert it to iteration or bounded depth.

intermediatedebuggingjavajvm

Symptoms

  • java.lang.StackOverflowError with thousands of repeated frames
  • The stack trace repeats the same method
  • Serialization or toString recurses infinitely
  • The error appears only for deep or cyclic data

Possible causes

  • A recursive method lacks a correct base case
  • Mutually recursive calls never terminate
  • Cyclic object graphs recurse during toString, equals or serialization
  • Deep but finite recursion exceeds the stack size

Diagnosis steps

Read the repeated frames

App 2>&1 | grep -A5 StackOverflow | head -20

Interpretation

Identical frames repeating identify the recursive call. Mutual recursion shows two methods alternating.

Check the base case

grep -n "return \|if (" Depth.java

Interpretation

A missing or unreachable base case is the usual defect; verify the condition uses the right comparison.

Handle cyclic graphs

grep -n "toString\|equals\|hashCode" Model.java

Interpretation

Generated methods that recurse through object graphs need cycle handling or identity-based fallbacks.

Root cause

Recursion descended without reaching a terminating base case, exhausting the thread stack, because the base condition was wrong or the data contained a cycle or excessive depth.

Fix

Correct the base case, convert deep recursion to an explicit stack or loop, add cycle detection for graph traversal, and raise the stack size only as a stopgap after fixing the logic.

Verify the fix

Rerun with the previously failing input and confirm completion, and test a deeply nested or cyclic case to confirm the new limit or cycle handling works.

Symptom walkthrough

The runtime throws StackOverflowError and the trace repeats the same method or two methods alternating. This signals unbounded recursion: either the base case is missing or unreachable, or the data is cyclic so toString, equals or serialization recurses forever. A finite but very deep structure can also exhaust the stack even with a correct base case.

Fix in practice

Fix the base condition first, since increasing stack size only delays the failure. Convert deep recursion into iteration with an explicit stack, and add cycle detection or identity-based handling for graphs. Reserve a larger thread stack for cases where depth is legitimately high and bounded.

Verification

Rerun with the input that failed and confirm completion, then exercise a cyclic or deeply nested case to confirm the new handling works. Both tests together confirm the recursion is now bounded.

Diagnostic commands

Check the behavior with commands like these:

App 2>&1 | grep -A5 StackOverflow | head -20
grep -n "return \|if (" Depth.java
grep -n "toString\|equals\|hashCode" Model.java