真实面经题目 · 原创解析

栈溢出一般抛什么异常?

栈溢出在 Java 中一般抛出 java.lang.StackOverflowError。严格说它不是 Exception,而是 Error,表示某个线程的调用栈空间被耗尽,常见原因是递归过深、递归没有终止条件、方法之间循环调用,或者单个栈帧过大。

出现于:阿里巴巴 · 算法

60 秒回答模板

如果说的是 Java,栈溢出一般抛 java.lang.StackOverflowError。它虽然经常被口语化地叫作异常,但严格来说不是 Exception,而是 Error,继承自 VirtualMachineError。它表示当前线程的虚拟机栈空间不够用了,通常由递归层数过深、递归终止条件错误、方法循环调用等导致。排查时主要看调用栈是否重复、递归边界是否正确、线程栈大小是否过小;可以通过优化递归、改成迭代、减少栈帧占用,必要时调整 -Xss 来处理。面试中还要区分 StackOverflowError 和 OutOfMemoryError:前者主要是调用深度问题,后者可能发生在堆、元空间、本地内存或线程创建等不同区域。

考点 直接结论
主线 异常体系
易错点 把 StackOverflowError 说成 Sta…

深入解析

01

直接结论

在 Java 中,栈溢出一般抛出 java.lang.StackOverflowError。题目如果问“抛什么异常”,更严谨的回答是:它不是 Exception,而是 Error。因此不要只答“抛异常”,要指出具体类型和它在 Throwable 体系中的位置。

02

异常体系

StackOverflowError 的继承链大致是 Throwable -> Error -> VirtualMachineError -> StackOverflowError。它和 RuntimeException、IOException 这类异常不是同一分支。Exception 通常表示程序可以预期、可以处理的异常情况,而 Error 通常表示虚拟机层面或资源层面的严重问题,应用代码一般不应该依赖捕获它来维持正常流程。

03

本质原因

每个 Java 线程都有自己的调用栈。方法调用时会创建栈帧,栈帧中保存局部变量表、操作数栈、返回地址等信息。当方法调用层级太深,新的栈帧无法再压入线程栈时,就会触发 StackOverflowError。它通常发生在某一个线程内,不等价于整个 JVM 堆内存耗尽。

04

触发场景

最常见原因是递归没有正确终止,例如递归出口缺失、出口条件永远无法满足、递归参数没有向终止方向变化。第二类是方法之间互相调用形成循环,例如 A 调 B,B 又调 A。第三类是递归本身有终止条件,但数据规模太大,调用深度超过线程栈容量。第四类是方法局部变量过多或栈帧较大,导致可承载的调用层数变少。

05

和 OOM 区别

StackOverflowError 表示线程调用栈溢出;OutOfMemoryError 表示内存分配失败,但具体区域可能不同。常见的 Java heap space 是堆空间不足,和对象分配、缓存、集合膨胀等有关;unable to create new native thread 往往和线程数量、系统资源有关;StackOverflowError 的典型特征则是错误栈里大量重复的方法调用。

06

线程栈大小

Java 线程栈大小可以通过 JVM 参数 -Xss 调整。-Xss 越大,单个线程理论上能支持更深的调用层级,但同样的进程内可创建的线程数量可能下降;-Xss 越小,线程更轻量,但深递归更容易溢出。因此调大 -Xss 只能作为特定场景的缓解手段,根本上仍应检查递归和调用结构是否合理。

07

排查路径

排查时先看错误栈。如果栈轨迹里反复出现同一个方法,通常是单方法递归问题;如果两个或多个方法交替出现,通常是循环调用问题。然后检查递归终止条件、参数推进方向、输入规模、是否存在对象序列化、toString、equals、hashCode 等方法里的间接递归。最后再评估是否需要把递归改为迭代,或者在明确合理的场景下调整 -Xss。

易错点

  • 把 StackOverflowError 说成 StackOverflowException,这是 Java 里常见但错误的说法。
  • 只回答“抛异常”,没有说明它是 Error 而不是 Exception,严谨性不足。
  • 把栈溢出和堆内存溢出混为一谈,误以为都只是内存不够。
  • 遇到栈溢出只想着调大 -Xss,没有先排查递归出口、循环调用和调用深度。
  • 忽略错误栈中的重复调用模式,没有利用调用栈定位具体递归链路。

面试官追问

StackOverflowError 是异常吗?

严格说不是 Exception,而是 Error。它们都属于 Throwable,所以口语里有人会把它叫异常,但面试回答应明确:StackOverflowError 继承自 Error,不是 Exception。

为什么递归容易导致栈溢出?

每次递归调用都会创建新的栈帧并压入当前线程的调用栈。如果递归层数太深,或者递归永远不能结束,栈帧会持续累积,直到线程栈空间耗尽。

如何解决栈溢出?

先检查递归终止条件是否正确,再检查递归参数是否向终止方向变化。如果递归深度天然很大,可以改成循环、显式栈、队列或动态规划等方式。

StackOverflowError 和 OutOfMemoryError 有什么区别?

StackOverflowError 主要是线程调用栈空间不足,表现为调用层级过深;OutOfMemoryError 是内存分配失败,可能发生在堆、元空间、本地内存或线程创建等场景。

能不能捕获 StackOverflowError?

语法上可以捕获,因为它是 Throwable 的子类。但工程上通常不建议依赖捕获它来恢复业务流程,更合理的做法是修正递归或调用结构。