看到java.lang.OutOfMemoryError: Java heap space报错,第一反应不是调大堆内存,而是紧盯堆栈最顶端几行定位问题起点;该错误表明堆内存耗尽,90%线上OOM属此类,需结合线程名、触发点方法、GC日志等快速区分泄漏、溢出或配置不当。

看到 java.lang.OutOfMemoryError: Java heap space 这类报错,第一反应不是急着调大堆内存,而是盯住堆栈最顶端那几行——那里藏着问题的“起点”。面试官想考察的,正是你能否从看似杂乱的 trace 中快速识别关键线索,区分是泄漏、溢出还是配置不当。
看异常类型和附带提示
OOM 不是一个错误,而是一类错误。不同后缀代表不同内存区域问题:
- Java heap space:堆内存耗尽,90% 的线上 OOM 属于这一类
- Metaspace(Java 8+)或 PremGen space(Java 7-):类元数据加载过多,常见于热部署、动态代理(如 CGLIB)、OSGi 场景
- unable to create new native thread:不是堆问题,是操作系统级线程资源枯竭,通常因线程数失控(如未用线程池、线程泄漏)
-
Direct buffer memory:NIO 使用
ByteBuffer.allocateDirect()分配堆外内存,但未显式清理或未设-XX:MaxDirectMemorySize
抓主线程与触发点方法
堆栈里真正有用的信息往往在倒数第 2–4 行,而不是最底下的 native 调用。重点看:
- 哪个线程抛出的?
main线程?http-nio-8080-exec-类型的工作线程?还是自定义线程名?线程名能反映业务上下文 - 最后一行非系统类的方法是谁?比如
UserService.processBatch(UserService.java:45)—— 这极可能是问题源头,要立刻检查该方法是否在循环中创建大对象、缓存未清理、流未关闭 - 有没有重复出现的类或包?比如连续几行都是
org.springframework.cglib...,提示可能在大量生成代理类
结合 GC 日志交叉验证
单看堆栈不够,必须搭配 GC 日志判断是“真缺内存”还是“回收失败”:
- 如果日志里频繁出现
Full GC且每次回收后老年代占用率仍 >95%,大概率是内存泄漏 - 如果
GC overhead limit exceeded报错,说明 JVM 把 98% 时间花在 GC 上却只回收不到 2% 内存,属于典型的“回收不动”,需查对象生命周期 - 没有 GC 日志?提醒面试官:生产环境必须加
-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
不依赖工具也能初步定性
即使没 dump 文件、没 MAT,也能从堆栈+现象做合理推测:
- 服务启动几分钟就 OOM → 检查静态集合初始化、Spring Bean 初始化逻辑(如预加载全量缓存)
- 运行几小时后缓慢恶化 → 典型内存泄漏,重点关注监听器注册未注销、ThreadLocal 未 remove、缓存 key 无过期策略
- 高并发瞬间崩掉 → 可能是单次请求分配超大对象(如导出百万行 Excel 生成大 byte[]),或是线程池拒绝策略不当导致任务堆积















