基于本文回答
0
评论

什么是内存溢出(OOM)和栈溢出(StackOverflow)?

知识点图片

内存溢出(OOM - Out Of Memory)栈溢出(Stack Overflow)是软件开发(特别是像 Java、C++ 这种语言)中两种常见的内存错误。

虽然它们都涉及到“内存不够用了”,但发生的位置原因表现形式完全不同。

以下是通俗易懂的详细解释:


1. 内存溢出(OOM - Out Of Memory)

简单一句话: 你的仓库(堆内存)装满了,再也塞不进任何新东西了。

  • 发生区域: 堆内存(Heap)
  • 本质: 程序运行过程中,申请的内存大于系统能够提供的内存。
  • 常见原因:
    1. 内存泄漏(Memory Leak): 最常见的原因。程序创建了对象,用完后没有释放(或者垃圾回收器无法回收),导致垃圾越堆越多,最后把内存占满了。
    2. 一次性加载过多数据: 例如,试图把一张 500MB 的高清图片或一个几百万行的数据库表一次性全部加载到内存中。
    3. 内存分配过小: 程序的启动参数设置的内存太小(例如 JVM 的 -Xmx 参数),无法满足正常的业务需求。
  • 典型代码场景(Java为例):
    java
    // 不断往 List 里加对象,且不释放,最终撑爆堆内存
    List<Object> list = new ArrayList<>();
    while (true) {
        list.add(new byte[1024 * 1024]); // 每次加1M
    }
    // 报错:java.lang.OutOfMemoryError: Java heap space

2. 栈溢出(Stack Overflow)

简单一句话: 你的盘子叠得太高了(方法调用层级太深),直接倒塌了。

  • 发生区域: 栈内存(Stack)
  • 本质: 线程请求的栈深度大于虚拟机所允许的深度。
    • 注:栈内存主要用来存储方法的调用链、局部变量、方法参数等。每调用一个方法,就会压入一个“栈帧”。
  • 常见原因:
    1. 无限递归(Infinite Recursion): 方法自己调用自己,且没有终止条件(死循环)。
    2. 递归过深: 虽然有终止条件,但层级太深(比如递归计算斐波那契数列的第100万项),超过了栈的容量。
    3. 循环依赖: A方法调B,B方法调A,无限循环。
  • 典型代码场景(Java为例):
    java
    public void methodA() {
        methodA(); // 自己调自己,没有出口
    }
    // 报错:java.lang.StackOverflowError

3. 核心区别对比表

特性 内存溢出 (OOM) 栈溢出 (Stack Overflow)
发生位置 堆 (Heap) 栈 (Stack)
存储内容 对象实例、数组、大数据 方法调用帧、局部变量、程序计数器
形象比喻 仓库塞满了,放不下新货物 叠罗汉叠太高,碰到天花板或塌了
主要原因 内存泄漏、大数据加载、内存配置不足 死递归、递归层级过深
解决思路 1. 检查代码是否有内存泄漏
2. 增加堆内存配置 (-Xmx)
3. 避免一次性加载大文件
1. 检查递归终止条件
2. 将递归改为循环
3. 极少数情况需增加栈大小 (-Xss)

4. 总结与排查建议

  • 遇到 OOM:

    • 首先怀疑内存泄漏。使用工具(如 JProfiler, MAT, VisualVM)分析内存快照(Heap Dump),看是哪个对象占用了大量空间且无法被回收。
    • 如果是处理大数据,考虑分批处理(流式处理),不要一次性 Load All
  • 遇到 StackOverflow:

    • 99% 的情况是代码逻辑错误
    • 检查代码里有没有写递归?如果有,检查终止条件(Base Case)是否正确。
    • 检查是否有两个方法互相调用形成了死循环。
右滑查看面试常问