内存溢出解决思路
在正式的线上项目中,如果遇到了OOM,解决思路需要分为3个关键步骤:
立即恢复服务,尽量保留现场
基于线程信息快速定位问题原因
修复并复盘,建立代码审核等机制,防止复发
第一步 恢复服务&保留现场
恢复服务
在线上的项目中,尤其是对于面向用户的服务,最紧急的的事情是恢复服务,而不是查询问题
-
重启服务:这是最快最有效的临时解决方案,目的是为了恢复服务的稳定性,避免对用户造成更大影响。
-
熔断降级:如果是微服务架构,可以将出问题的示例从注册中心去除,避免影响扩散。
保留现场
预先保留日志:在服务启动时就使用-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof 参数,可以保证在发生OOM时自动生成Heap Dump文件。
如果没有提前设置:可以在服务重启前尝试使用jmap手动导出一份(jmap -dump:format=b,file=heap.hprof <pid>)
第二步 问题定位与分析
常见的OOM类型
比较常见的OOM问题一般是如下类型:
java.lang.OutOfMemoryError: Java heap space: 最常见,堆内存不足。对象实例过多无法回收。
java.lang.OutOfMemoryError: Metaspace: 元空间(方法区)不足。加载了太多类,常见于动态生成类(如CGLib)、反射或Spring的热部署。
java.lang.OutOfMemoryError: Unable to create new native thread: 创建的线程数超过系统限制。
java.lang.OutOfMemoryError: Direct buffer memory: 直接内存(NIO使用的堆外内存)不足。
java.lang.OutOfMemoryError: GC overhead limit exceeded: GC效率过低,98%的时间在GC但只回收了不到2%的内存,其实就是堆内存溢出的前兆。
常用工具
常用的java编辑器一般都会有dump文件分析工具:如IDEA中的Profiler
拿IDEA举例子,直接使用idea打开dump文件,打开后就会出现文件分析结果:如最大对象,GC根,摘要等重要信息
排查方式就是 在最大对象中查找比较常见的OOM类 有没有如:
静态集合:如static Map 缓存了数据但没有做大小限制和淘汰策略
未关闭的资源:如数据库连接,文件流,网络连接,ThreadLocal等
监听器:监听器在不使用后,没有正确关闭导致
第三方库或者中间件:可能有内存泄漏的bug

修复验证
修复后,使用压测工具(JMeter)模拟线上流量,观察内存变化。
问题预防
对于产生OOM的代码能上线,本质上是代码审查的机制不完善,应该根据业务紧急情况制定不同的代码审查,如不着急上线的功能必须进行代码审查后才能上线,紧急上线的需求,可以在上线后进行代码审查,及时发现问题,在问题产生前修复上线。