Java中的StackOverflowError从原理到实战指南(栈溢出的 4 种典型场景)

 更新时间:2026年03月07日 10:33:21   作者:会员源码网  
java.lang.StackOverflowError 是 JVM 抛出的错误(Error) ,不是普通异常(Exception),本文给大家介绍Java中的StackOverflowError从原理到实战指南(栈溢出的 4 种典型场景),感兴趣的朋友跟随小编一起看看吧

一、什么是 StackOverflowError?

java.lang.StackOverflowError 是 JVM 抛出的错误(Error) ,不是普通异常(Exception)。它表示:当前线程的虚拟机栈空间被耗尽,无法再分配新的栈帧

核心原理

  1. 每个线程有独立的虚拟机栈,默认大小约 1MB(可通过 -Xss 调整)。
  2. 每调用一个方法,JVM 会创建一个栈帧压入栈中,存放局部变量、返回地址、操作数栈等。
  3. 方法执行完毕,栈帧出栈。
  4. 压栈速度远大于出栈,栈空间被占满,就抛出 StackOverflowError。

一句话总结:方法调用层级太深,栈被撑爆了。

二、栈溢出的 4 种典型场景(附代码)

1. 无限递归(最常见)

没有正确的递归出口,方法无限调用自身。

public class StackOverflowDemo {
    public static void recursion() {
        recursion(); // 无终止条件
    }
    public static void main(String[] args) {
        recursion();
    }
}

报错:直接抛出 StackOverflowError,堆栈信息疯狂重复同一行。

2. 递归深度过大(有出口但太深)

public static int deepRecursion(int n) {
    if (n == 0) return 0;
    return deepRecursion(n - 1);
}
// 调用:deepRecursion(100000) → 栈溢出

默认栈深度大约在 1000~2000 层,超过就会爆。

3. 方法循环调用(A→B→A→B…)

public class CycleCall {
    public static void a() { b(); }
    public static void b() { a(); }
    public static void main(String[] args) { a(); }
}

本质和无限递归一样,形成调用环。

4. 超大局部变量(少见但致命)

栈不仅存调用链,还存局部变量。一次性在栈上分配超大数组会直接爆栈。

public static void bigLocalVar() {
    int[] huge = new int[1024 * 1024]; // 栈上分配,直接溢出
}

注意:对象实例在堆,数组引用在栈,但超大基本类型数组会占满栈空间。

三、关键知识点:Error vs Exception

  • Exception:可捕获、可恢复(空指针、下标越界等)。
  • Error:系统级错误,不建议捕获,捕获也无法恢复。
  • StackOverflowError 一旦发生,线程已无法正常执行,catch 住也没意义。

四、如何快速定位 StackOverflowError?

1. 看异常堆栈(最直接)

堆栈信息会大量重复某几行,那就是递归 / 循环调用的根源。示例:

plaintext

Exception in thread "main" java.lang.StackOverflowError
at com.demo.StackOverflowDemo.recursion(StackOverflowDemo.java:5)
at com.demo.StackOverflowDemo.recursion(StackOverflowDemo.java:5)
at com.demo.StackOverflowDemo.recursion(StackOverflowDemo.java:5)
...

2. 用 jstack 抓线程栈

jstack -l <pid>

找到 RUNNABLE 且栈超长的线程,定位问题方法。

3. IDE 断点调试

在可疑递归 / 循环处打断点,观察调用次数与条件是否正常收敛。

五、解决方案:从根本到临时

1. 修复递归(首选)

  • 必须有明确终止条件
  • 每次递归必须向终止条件逼近
// 正确递归示例
public static int factorial(int n) {
    if (n <= 1) return 1; // 出口
    return n * factorial(n - 1); // 收敛
}

2. 递归改迭代 / 循环(根治深度问题)

用循环 + 集合模拟栈,彻底摆脱栈深度限制。

// 迭代替代递归
public static int loopFactorial(int n) {
    int res = 1;
    for (int i = 2; i <= n; i++) res *= i;
    return res;
}

3. 增大栈空间(临时方案)

通过 JVM 参数 -Xss 调整线程栈大小:

-Xss256k
-Xss1m
-Xss2m

不建议滥用:栈过大会导致线程数减少,引发 OOM,只能作为临时兼容。

4. 解除循环调用

梳理依赖关系,避免 A↔B 无限调用。

5. 大对象移到堆

把栈上超大局部变量改成对象,分配到堆。

六、栈溢出 vs 堆溢出(OOM)

表格

对比项StackOverflowErrorOutOfMemoryError
发生区域虚拟机栈堆 / 方法区 / 直接内存
原因方法调用太深、栈帧太多堆内存不足,无法分配对象
触发场景递归、循环调用内存泄漏、大对象、加载类过多
JVM 参数-Xss-Xms -Xmx

七、企业级避坑建议

  1. 慎用递归:能用迭代就不用递归。
  2. 递归必加出口与深度限制
  3. 禁止在栈上声明超大数组
  4. 框架使用注意:MyBatis 嵌套查询、Spring AOP 嵌套切面、Lombok 构造循环都可能隐式递归。
  5. 上线前压测:极端数据(深度树、超长链)必测。

八、总结

StackOverflowError 本质就是栈空间被方法调用占满

  • 90% 是递归问题
  • 8% 是循环调用
  • 2% 是栈配置 / 局部变量问题

解决思路:先看堆栈定位重复代码 → 修复递归 / 解除循环 → 必要时调整 -Xss

只要掌握原理与排查流程,栈溢出就是最容易解决的错误之一。

到此这篇关于Java中的StackOverflowError从原理到实战指南(栈溢出的 4 种典型场景)的文章就介绍到这了,更多相关Java StackOverflowError原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现Excel表单控件的添加与删除

    Java实现Excel表单控件的添加与删除

    本文通过Java代码示例介绍如何在Excel表格中添加表单控件,包括文本框、单选按钮、复选框、组合框、微调按钮等,以及如何删除Excel中的指定表单控件,需要的可以参考一下
    2022-05-05
  • JPA如何使用entityManager执行SQL并指定返回类型

    JPA如何使用entityManager执行SQL并指定返回类型

    这篇文章主要介绍了JPA使用entityManager执行SQL并指定返回类型的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • java 抽象类的实例详解

    java 抽象类的实例详解

    这篇文章主要介绍了java 抽象类的实例详解的相关资料,希望通过本大家能理解掌握这部分内容,需要的朋友可以参考下
    2017-09-09
  • Java二分算法题目练习实战教程

    Java二分算法题目练习实战教程

    二分查找(Binary Search)是一种非常高效的查找算法,它在有序数组或有序列表中通过反复将搜索范围分为两半来查找目标元素,这篇文章主要介绍了Java二分算法题目练习的相关资料,需要的朋友可以参考下
    2025-11-11
  • Springboot自定义注解&传参&简单应用方式

    Springboot自定义注解&传参&简单应用方式

    SpringBoot框架中,通过自定义注解结合AOP可以实现功能如日志记录与耗时统计,首先创建LogController和TimeConsuming注解,并为LogController定义参数,然后,在目标方法上应用这些注解,最后,使用AspectJ的AOP功能,通过切点表达式定位这些注解
    2024-10-10
  • java使用spring实现发送mail的方法

    java使用spring实现发送mail的方法

    这篇文章主要介绍了java使用spring实现发送mail的方法,涉及java基于spring框架发送邮件的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-10-10
  • Java 判断实体对象及所有属性是否为空的操作

    Java 判断实体对象及所有属性是否为空的操作

    这篇文章主要介绍了Java 判断实体对象及所有属性是否为空的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • java实现去除ArrayList重复字符串

    java实现去除ArrayList重复字符串

    本文主要介绍了java实现去除ArrayList重复字符串,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-09-09
  • springboot如何将http转https

    springboot如何将http转https

    这篇文章主要介绍了springboot如何将http转https,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • java中Executor,ExecutorService,ThreadPoolExecutor详解

    java中Executor,ExecutorService,ThreadPoolExecutor详解

    这篇文章主要介绍了java中Executor,ExecutorService,ThreadPoolExecutor详解的相关资料,需要的朋友可以参考下
    2017-02-02

最新评论