StackOverflowError是栈空间耗尽所致,主因是无限递归或栈帧过大;OutOfMemoryError是堆/元空间/直接内存不足所致,需据错误信息细分定位;二者发生时机、排查方式及JVM参数均不同。

StackOverflowError 通常是因为递归太深或栈帧过大
Java 虚拟机为每个线程分配固定大小的栈空间(默认一般 1MB 左右),StackOverflowError 就是这个空间被耗尽时抛出的。最常见原因是无限递归,比如 factorial(n) 忘了写 base case,或者递归深度天然很大(如遍历深度 10000 的树)却没改栈大小。
- 不是所有递归都会触发——JVM 会做尾调用优化(但 Java 8+ 官方不支持尾递归优化,
javac和 HotSpot 都不处理) - 局部变量过多、方法参数太长、匿名类/lambda 捕获大量外部变量,也会让单个栈帧变大,加速栈溢出
- 可通过
-Xss512k减小栈大小来快速复现问题,或用-Xss2m加大来临时缓解(但治标不治本)
OutOfMemoryError 的常见子类型和定位方式
OutOfMemoryError 是堆内存(或元空间、直接内存等)不足时的统称,必须看完整错误信息才能判断具体原因。常见形式有:
-
java.lang.OutOfMemoryError: Java heap space:堆对象太多且 GC 清不掉,典型如缓存未设上限、集合不断add不清理、大对象长期持有引用 -
java.lang.OutOfMemoryError: Metaspace:类加载太多(尤其动态生成类场景,如 Spring Boot + CGLIB + 频繁 reload)、或-XX:MaxMetaspaceSize设得太小 -
java.lang.OutOfMemoryError: Direct buffer memory:用了ByteBuffer.allocateDirect()却没调cleaner或free(),或-XX:MaxDirectMemorySize限制过严
建议启动时加 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof,OOM 后直接分析 dump 文件,比猜快得多。
两者关键区别:栈 vs 堆,错误时机与排查路径完全不同
StackOverflowError 发生在方法调用过程中,JVM 还能正常运行,甚至可以 catch(虽然不推荐);而多数 OutOfMemoryError(尤其是堆溢出)发生时,GC 已经频繁失败,系统响应迟滞,catch 住也很难安全恢复。
JTopCMS基于JavaEE自主研发,是用于管理站群内容的国产开源软件(CMS),能高效便捷地进行内容采编,审核,模板制作,用户交互以及文件等资源的维护。安全,稳定,易扩展,支持国产中间件及数据库,适合建设政府,教育以及企事业单位的站群系统。 系统特色 1. 基于 JAVA 标准自主研发,支持主流国产信创环境,国产数据库以及国产中间件。安全,稳定,经过多次政务与企事业单位项目长期检验,顺利通过
立即学习“Java免费学习笔记(深入)”;
-
StackOverflowError的 stack trace 通常很长且高度重复(比如几百行都是at com.example.Calc.compute(Calc.java:23)) -
OutOfMemoryError: Java heap space的 stack trace 往往很短,最后一行常是new或ArrayList.add这类分配点,真正根源在上游 - JVM 参数干预方向相反:调栈大小用
-Xss,调堆用-Xmx,调元空间用-XX:MaxMetaspaceSize
一个容易被忽略的交叉点:递归创建大量对象也可能触发 OOM 而非 SOE
如果递归函数里每层都新建大对象(比如每次 new byte[1024*1024]),可能还没等栈满,堆就先爆了。这时候看到的是 OutOfMemoryError: Java heap space,但根源仍是递归失控。
public void badRecursion(int n) {
if (n <= 0) return;
byte[] data = new byte[1024 * 1024]; // 每层占 1MB 堆
badRecursion(n - 1);
}这种混合场景下,光看错误类型会误判。得结合线程 dump + heap dump + 代码逻辑一起看——栈深度、对象数量、GC 日志三者缺一不可。









