应先诊断内存问题根源而非盲目调大-xmx:通过gc日志、jstat查老年代增长趋势、jmap分析对象分布,区分是流量突增、配置不足还是内存泄漏;再结合mat分析堆转储定位静态集合、threadlocal等泄漏源。

直接调高堆内存参数往往只能缓解,不能根治。关键得先判断是配置不足、流量突增,还是代码真有泄漏——三类原因对应不同处理路径。
看日志和监控,快速定位类型
收到 java.lang.OutOfMemoryError: Java heap space 后,别急着改 -Xmx。先查两件事:
- 错误是否集中在某次活动/上线后突然出现?比如秒杀开始5分钟内爆发 → 很可能是业务流量超预期;
- 错误是否随运行时间推移越来越频繁,且 Full GC 后堆内存回收极少?→ 倾向于内存泄漏;
- 错误发生前是否有单次大对象分配(如 new byte[1024*1024*500])?→ 可能是超大对象申请失败。
按场景调整 JVM 参数
参数不是越大越好,要匹配实际压力:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 日常服务:设 -Xms 和 -Xmx 相等(如
-Xms2g -Xmx2g),避免堆动态扩容带来的停顿; - 批处理任务:可临时加大堆,例如
-Xmx4g,但需配合 -XX:+HeapDumpOnOutOfMemoryError 自动导出堆快照; - 容器环境:必须设置 -XX:+UseContainerSupport,否则 JVM 可能无视容器内存限制,导致 OOM 被 kill。
用工具确认泄漏点,不止靠猜
光看代码很难发现隐藏引用。推荐组合使用:
- 线上轻量排查:加 -XX:+PrintGCDetails -Xloggc:gc.log,观察 GC 频率与回收量比值;
- 定位对象堆积:用 jstat -gc
查看老年代使用率是否持续攀升; - 深度分析堆快照:用 Eclipse MAT 打开 hprof 文件,重点关注 “Dominator Tree” 和 “Leak Suspects” 报告;
- 常见泄漏源:静态集合未清理、ThreadLocal 没 remove、监听器注册后未反注册、缓存未设淘汰策略。
代码层规避高频风险
很多 OOM 其实源于几个固定写法习惯:
- 数据库查询不加 limit 或分页,一次拉几十万条进内存;
- 用 static Map
做缓存,却从不清理过期项; - 流式处理时,把 InputStream 转成 byte[] 而非用缓冲区逐段读;
- 日志打印对象时写
log.info("obj={}", hugeObject),触发 toString() 加载全部字段。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










