内存溢出(oom)是内存被填满触发jvm报错,内存泄漏是对象因意外引用无法回收致可用内存持续减少;防范需从编码习惯、资源管控和可观测性入手,包括规范资源释放、避免静态强引用、合理配置jvm参数及借助工具链检测。

内存溢出(OOM)和内存泄漏是 Java 系统上线后最常引发线上故障的两类问题,二者表现相似但成因不同:内存溢出是堆或元空间等区域被真正“填满”,触发 JVM 报错;内存泄漏则是对象本该被回收却长期被意外引用,导致可用内存持续减少,最终诱发 OOM。防范的关键不在事后排查,而在编码习惯、资源管控和可观测性建设。
明确生命周期,及时释放非托管资源
Java 的垃圾回收只管理堆内对象,不自动释放文件句柄、数据库连接、网络 Socket、线程、缓存项等非堆资源。若未显式关闭或清理,极易造成泄漏。
- 所有实现 AutoCloseable 的资源(如 InputStream、Connection、PreparedStatement),必须用 try-with-resources 语法,避免 finally 中手动 close 的遗漏
- 线程池需主动调用 shutdown() 或 shutdownNow(),尤其在 Spring 容器关闭时通过 @PreDestroy 或实现 DisposableBean 保证清理
- 本地缓存(如 Guava Cache、Caffeine)应设置合理的 maximumSize 和 expireAfterWrite,避免无限制堆积
- 监听器、回调、静态集合类(如 static Map
)需确认注册/添加后有对应反注册/移除逻辑,尤其注意 Activity、Fragment、Listener 等上下文强引用场景
慎用静态引用与长生命周期对象持有短生命周期对象
静态变量生命周期与类加载器一致,一旦持有业务对象(如 Activity、UserContext、RequestDTO),会阻止整个对象图被回收,是内存泄漏高发区。
- 避免将 this、Activity、Context 直接存入 static 字段;如需全局访问,改用 ApplicationContext,并确保不隐式持有 View 或 Fragment 引用
- 内部类(尤其是匿名类、Lambda)默认持外部类引用;若该内部类被异步任务、线程池、静态容器长期持有,会导致外部实例无法回收 —— 改用 static 内部类 + WeakReference 模式
- Handler 在 Activity 中使用时,若消息队列未清空且 Handler 是非静态的,可能延迟 Activity 回收;推荐使用 static Handler + WeakReference
或在 onDestroy() 中调用 removeCallbacksAndMessages(null)
合理配置 JVM 参数并建立内存监控基线
参数不是越大越好,关键在于匹配应用特征,并留出缓冲空间。盲目增大堆内存只会延缓 OOM,掩盖真实泄漏。
- 设置 -Xms 和 -Xmx 相等,避免运行时扩容带来 GC 波动;新生代占比建议 30%~40%,可通过 -XX:NewRatio 或 -Xmn 调整
- 启用 -XX:+HeapDumpOnOutOfMemoryError 并指定 -XX:HeapDumpPath,确保 OOM 时自动生成堆转储(heap dump)供分析
- 开启 GC 日志:-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps,结合 Prometheus + Grafana 监控 GC 频率、耗时、晋升失败次数等指标
- 定期用 jstat 观察老年代使用趋势;若 Full GC 后老年代内存不下降,或老年代持续缓慢上涨,大概率存在内存泄漏
借助工具链实现日常检测与上线前卡点
靠人工 Review 很难发现隐蔽引用链,需将检测左移到开发与测试阶段。
- 单元测试中用 WeakReference 断言对象是否可被回收(配合 System.gc() + await);对缓存、监听器等模块做生命周期验证
- CI 流水线集成 SpotBugs(原 FindBugs)或 SonarQube,启用 BC_UNCONFIRMED_CAST、IS2_INCONSISTENT_SYNC、MSF_MUTABLE_STATIC_FIELD 等内存相关规则
- 预发环境开启 JFR(Java Flight Recorder) 录制 1~2 小时,用 JDK Mission Control 分析对象分配热点、大对象生成、长时间存活对象来源
- 上线前必查 MAT(Memory Analyzer Tool)分析最近一次 heap dump:按 dominator tree 查看“Shallow Heap”大的对象,用 “Leak Suspects” 报告快速定位可疑引用链
不复杂但容易忽略 —— 多数内存问题源于一个未关闭的流、一个未清理的监听器、一行静态 map 的 put。把资源生命周期意识融入每次编码,再辅以轻量工具卡点,就能大幅降低线上 OOM 风险。











