直接定位异常对象在堆和元空间中的分布特征可显著缩短根因判断时间:堆溢出聚焦大数组、静态缓存、未关闭资源及threadlocal泄漏;元空间溢出则紧盯动态代理类加载、classloader泄漏及类卸载失败;需联动分析堆dump与元空间引用链,按报错类型快速分流并交叉验证。

看报错信息,先锁定内存区域
不同 OOM 报错对应完全不同的内存子系统,必须第一时间分流:
- java.lang.OutOfMemoryError: Java heap space → 全力排查堆中对象:关注大数组、长生命周期集合、未关闭的流/连接、静态缓存膨胀
- java.lang.OutOfMemoryError: Metaspace → 聚焦类加载行为:检查动态代理生成(如 CGLIB、MyBatis Mapper)、OSGi 插件热部署、自定义类加载器泄漏、长时间未重启服务
- java.lang.OutOfMemoryError: GC overhead limit exceeded → 不是单纯缺内存,而是堆里塞满了“回收不动”的对象,通常伴随 Metaspace 增长或 ClassLoader 泄漏,需同步查堆 + 元空间引用链
堆内对象检索:按“存活动机”分层扫描
别一上来就找 biggest objects。先问:这些对象为什么还活着?按 GC Roots 引用动机分类,效率更高:
-
静态持有:检查
java.util.HashMap、ConcurrentHashMap、ArrayList等静态字段,尤其注意缓存类(如CacheManager)未设淘汰策略 -
线程局部:查看
ThreadLocal变量(特别是ThreadLocal<connection></connection>或ThreadLocal<dateformat></dateformat>),未 remove 导致线程复用时持续累积 -
未释放资源:搜索
java.io.FileInputStream、java.net.Socket、org.apache.http.impl.conn.PoolingHttpClientConnectionManager等实例,确认 close() 是否被调用
元空间对象检索:盯住 ClassLoader 和 Class 实例
元空间不存实例,只存类元数据。真正占空间的是 java.lang.Class 对象及其加载器。重点查:
- 每个
ClassLoader实例加载了多少个Class(MAT 中用 “List objects > with incoming references” 查 ClassLoader 的 loadedClasses 字段) - 是否存在大量相同类名但不同 ClassLoader 加载的类(典型于 Spring Boot DevTools、Tomcat WebAppClassLoader 隔离失败)
- 是否有
sun.misc.Launcher$AppClassLoader持有不该持有的 Class(说明类卸载失败,常因反射引用、JNDI 绑定、MBean 注册未清理)
联动分析:堆 + 元空间交叉验证
单一视图容易误判。例如:
- 发现堆里有大量
com.example.service.UserService$$EnhancerBySpringCGLIB$$xxx实例 → 立即去元空间查对应的ClassLoader是否也在增长 → 若是,基本确定是 Spring AOP 代理未清理或上下文未关闭 - GC 日志显示频繁 Metadata GC Threshold → 同时堆 dump 中发现大量
java.lang.ref.Finalizer实例 → 很可能 finalizer 队列积压,阻塞了 ClassLoader 卸载(因为 Finalizer 持有对象引用,间接阻止其 ClassLoader 被回收)











