Java堆内存溢出需先区分内存溢出与泄漏,通过日志确认错误类型,配置参数自动生成堆转储,用MAT分析泄漏源头,聚焦静态集合、ThreadLocal等典型问题,并辅以合理JVM调参。

看到 java.lang.OutOfMemoryError: Java heap space,说明堆内存真不够用了。这不是简单加个 -Xmx 就能根治的问题,得先分清是“东西太多装不下”(内存溢出),还是“垃圾没扫走堆满了”(内存泄漏)——前者要优化逻辑,后者得修代码。
看日志,确认是不是堆溢出
错误信息里明确带 Java heap space,就锁定是堆区问题。别被其他 OOM 类型干扰,比如 Metaspace 或 unable to create new native thread,它们处理方式完全不同。
抓快照,别等服务崩了才动手
启动 JVM 时加上这两个参数,让问题发生时自动留证据:
-
-XX:+HeapDumpOnOutOfMemoryError -
-XX:HeapDumpPath=/path/to/dumps/
这样 OOM 一触发,.hprof文件就生成好了,不用手动jmap,也避免进程退出导致 dump 不完整。
用 MAT 看真正该死的对象
打开 .hprof 文件后,别只盯着“谁占内存最多”。重点做三件事:
- 运行 Leak Suspects Report,它会标出几个高风险对象组
- 切到 Dominator Tree,按 Retained Heap 排序,找业务相关的类(比如
OrderCache、UserSessionManager) - 对可疑对象右键 → Path to GC Roots → 勾选 exclude all weak/soft references,看强引用链终点在哪
常见泄漏源头:静态集合没清理、ThreadLocal 没 remove、监听器注册后没注销、线程池里的 Runnable 持有大对象引用。
查代码,盯住这几类典型写法
- 缓存没设上限或过期策略(比如
static Map<String, byte[]> cache = new HashMap<>()一直 put 不 clear) - 日志打太长(一行几 MB 的 JSON 字符串反复拼接)
- 流式处理变全量加载(
listAllUsers()返回百万级 List 而不是分页或 Stream) - 第三方 SDK 使用不当(如某些 HTTP 客户端未关闭连接池,底层缓存越积越多)
调参只是辅助,不是解药
- 设
-Xms和-Xmx相同值(如-Xms2g -Xmx2g),避免 GC 时频繁扩容缩容 - 配
-XX:+UseG1GC(尤其堆大于 4G 时),G1 更适合控制停顿和回收效率 - 但参数再优,挡不住一个无限往
ArrayList里 add 的 while(true) 循环
不复杂但容易忽略
立即学习“Java免费学习笔记(深入)”;


















