应通过生成并分析堆快照定位jvm内存溢出根源:启用-xx:+heapdumponoutofmemoryerror自动捕获,或用jmap手动导出;再用mat查看dominator tree、leak suspects和path to gc roots识别泄漏点或优化方向。

遇到 Java 堆内存溢出(java.lang.OutOfMemoryError: Java heap space)时,关键不是看异常堆栈,而是拿到当时堆的“快照”——也就是 .hprof 文件,再用专业工具定位谁占得多、为什么没被回收。
一、用 jmap 抓取堆快照(运行中或刚崩溃后)
生产环境常用方式是手动触发,尤其当 OOM 尚未发生但内存持续上涨时:
- 先查 Java 进程 ID:
jps -l或ps aux | grep java - 执行导出命令:
jmap -dump:format=b,file=heap.hprof <pid></pid>
例如:jmap -dump:format=b,file=/tmp/heap.hprof 12345 - 若进程卡死无响应,加
-F强制执行:jmap -F -dump:format=b,file=heap.hprof 12345
⚠️ 注意:jmap 会暂停目标 JVM 几秒,线上高峰慎用;如需低干扰,可先用 jstat -gc <pid> 1000 5</pid> 观察 GC 频率与老年代增长趋势,确认是否真为堆问题。
二、用 Eclipse MAT 打开并快速定位问题
下载安装 Eclipse Memory Analyzer (MAT),打开 .hprof 文件后重点看三个视图:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
-
Dominator Tree:按“Retained Heap”降序排列,直接看到哪些对象集合实际占用了最多内存。比如一个静态
ConcurrentHashMap缓存了 20 万条订单数据,就会排在最上面。 - Leak Suspects Report:点击右上角“Reports” → “Leak Suspects”,MAT 自动扫描并高亮可疑泄漏点,附带引用路径摘要和占比说明。
-
Histogram + Path to GC Roots:在 Histogram 中筛选高频类(如
byte[]、String、自定义 DTO),右键某个实例 → “Path to GC Roots” → 选 “exclude weak/soft references”,看清它被哪个 static 字段、ThreadLocal 或监听器意外强持有。
三、常见线索与对应动作
分析时留意这几类典型模式:
- 大量
byte[]或char[]:检查日志拼接、JSON 序列化未关闭流、Base64 解码缓存、或大文件上传未流式处理 - 同一业务类实例数暴增(如
OrderProcessor达数十万):查是否在循环里 new 且放入静态集合、或 Spring Bean 作用域配置错误(如 prototype 被注入 singleton) - 线程相关对象堆积(如
java.lang.Thread、java.util.concurrent.ThreadPoolExecutor):确认线程池未正确 shutdown,或 ThreadLocal 没有 remove 导致 Value 泄漏 - GC Roots 显示被
java.util.logging.Logger或第三方 SDK 的静态容器引用:升级 SDK 版本或主动清理注册回调
四、区分泄漏 vs 配置不足,决定修复方向
结论不同,动作完全不同:
- 确认是内存泄漏:对象生命周期已结束,却因强引用链残留。修复重点是切断非必要引用——清空 static Map、注销事件监听器、显式 close 资源、ThreadLocal.remove()
- 属于正常占用但堆太小:比如缓存必须存 50 万用户画像,当前
-Xmx2g确实不够。此时应合理扩容(如-Xmx4g),同时评估是否可引入堆外缓存(Caffeine off-heap / Redis)
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










