内存溢出是“内存不够用”的结果,内存泄漏是“该回收却没回收”的过程;前者因申请时空间不足直接抛OutOfMemoryError崩溃,后者因强引用滞留导致对象无法被GC回收、内存缓慢增长最终可能引发OOM。

内存溢出(OOM)和内存泄漏(Memory Leak)本质不同:前者是“内存不够用”的结果,后者是“该回收却没回收”的过程。
内存溢出是资源耗尽的临界崩溃
当程序申请内存时,JVM 或操作系统已无法提供足够空间,就会直接抛出 OutOfMemoryError(如 Java heap space、Metaspace、Direct buffer memory),程序强制终止。这不是缓慢积累的问题,而是瞬间发生的资源上限突破。
- 常见触发场景:一次性加载超大文件到 List、无限循环创建对象、递归过深导致栈帧撑满、动态生成大量类未限制元空间大小
- 关键特征:有明确异常堆栈、进程立即中断、可通过增大 JVM 参数(如
-Xmx)临时缓解,但治标不治本 - 注意:StackOverflowError 虽单独报错,但本质属于栈内存溢出,是 OOM 的一种特殊表现
内存泄漏是引用滞留的隐性浪费
对象本该被垃圾回收,却因被长生命周期对象意外持有(如 static 集合、未清理的 ThreadLocal、注册后未注销的监听器),导致 GC 无法回收——这些对象持续占用堆内存,但程序表面仍正常运行,只是内存使用量随时间推高。
- 典型泄漏源:静态容器缓存未清理、IO/DB 连接未 close、内部类隐式持外层引用、线程池中核心线程长期持有上下文对象
- 关键特征:无异常抛出、内存曲线缓慢爬升、长时间运行后才可能诱发 OOM、需借助内存分析工具(如 MAT、VisualVM)定位泄漏根因
- 一次泄漏影响微小,但高频或持续泄漏会逐步蚕食可用堆空间
二者最核心的联系在于因果链
内存泄漏本身不会立刻报错,但它会让有效内存持续缩水;当剩余可用堆空间低于新对象分配需求时,就从“慢性消耗”演变为“急性崩溃”,触发 OOM。也就是说:泄漏是常见诱因,但不是唯一原因。
- OOM 可能由泄漏引起,也可能源于合理业务逻辑 + 过小内存配置(例如正常导出 10 万条数据,但 -Xmx 只设了 64MB)
- 发现 OOM 后不能默认归咎于泄漏——要先看是单次巨量分配(如 new byte[2GB]),还是长期运行后才发生
- 一个稳定服务上线数周后突然 OOM,比刚启动就 OOM 更值得怀疑存在内存泄漏
判断与应对思路要分开对待
遇到内存问题,先看现象再定性质:
- 启动即 OOM 或操作某功能立刻 OOM → 优先查代码逻辑是否单次申请过大、JVM 参数是否过低
- 运行数小时/数天后 OOM、GC 频繁且老年代持续增长 → 重点排查泄漏,做堆转储(heap dump)分析对象引用链
- 监控显示老年代使用率缓慢上升、Full GC 后无法回落 → 强烈提示存在泄漏,尤其关注 java.lang.Class、java.util.HashMap$Node、org.springframework.context.support.GenericApplicationContext 等高频泄漏载体










