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原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot手写RestTemplate拦截器链,掌控HTTP请求实践

    SpringBoot手写RestTemplate拦截器链,掌控HTTP请求实践

    本文介绍了在Spring项目中使用RestTemplate时遇到的问题及解决方案,通过自定义拦截器链来实现请求的统一处理,包括添加认证Token、记录请求日志、设置超时时间、实现失败重试等,关键点在于拦截器链的顺序和响应流的处理,以及确保拦截器的线程安全
    2025-12-12
  • ReentrantLock 非公平锁实现原理详解

    ReentrantLock 非公平锁实现原理详解

    这篇文章主要为大家介绍了ReentrantLock 非公平锁实现原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 使用Spring Cloud Gateway实现动态路由的核心原理

    使用Spring Cloud Gateway实现动态路由的核心原理

    Spring Cloud Gateway 实现动态路由的核心是将路由规则从静态配置迁移到外部数据源,并通过事件机制实时刷新路由缓存,本文介绍使用Spring Cloud Gateway实现动态路由的核心原理,感兴趣的朋友跟随小编一起看看吧
    2025-10-10
  • java去除集合中重复元素示例分享 java去除重复

    java去除集合中重复元素示例分享 java去除重复

    这篇文章主要介绍了java去除集合中重复元素示例,大家参考使用吧
    2014-01-01
  • MyBatis Mapper 接口是如何找到并执行SQL的

    MyBatis Mapper 接口是如何找到并执行SQL的

    本文将为你揭开MyBatis这层神秘的面纱,深入剖析其接口与SQL的映射原理,并总结在Spring Boot环境下的最佳实践和常见陷阱,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • Java枚举类接口实例原理解析

    Java枚举类接口实例原理解析

    这篇文章主要介绍了Java枚举类接口实例原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Java聊天室之使用Socket实现传递对象

    Java聊天室之使用Socket实现传递对象

    这篇文章主要为大家详细介绍了Java简易聊天室之使用Socket实现传递对象功能,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以了解一下
    2022-10-10
  • mybatis plus in方法使用说明

    mybatis plus in方法使用说明

    这篇文章主要介绍了mybatis plus in方法使用说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • Spring Cloud Gateway实现分布式限流和熔断降级的示例代码

    Spring Cloud Gateway实现分布式限流和熔断降级的示例代码

    这篇文章主要介绍了Spring Cloud Gateway实现分布式限流和熔断降级的示例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-06-06
  • 微信开发协议小结

    微信开发协议小结

    通过本教程给大家分享微信开发协议小结的相关知识,非常不错,具有一定的参考借鉴价值,感兴趣的朋友一起看看吧
    2016-11-11

最新评论