JVM系列之OOM实战

工作以来,陆陆续续遇见Java应用OOM情况非常多了。每次未定位到原因时总是让人战战兢兢,特别是发生在交易频繁的系统身上,压力更大。因此,编写代码的时候,不时问问自己,我们这样写,会不会引发OOM。分享一篇关于OOM的经历,希望能给大家带来一点解决问题的灵感吧。

导致OOM的原因很多,有堆内存溢出,有元空间溢出,有栈溢出。但是堆内存溢出最为常见,其中堆内存溢出中以大对象或者常驻内存无法被回收(内存泄漏)导致最终内存溢出最为常见。当然,所谓的大对象也是相对虚拟机分配的内存来说的。但在编码的过程中,我们尽量避免大对象的产生。

大概在两年前的一个周五晚上,正好是交易相对繁忙的时候,笔者亲身经历一个生产OOM故障,至今印象十分深刻。因为交易超时触发告警,去机房一看日志和机器情况,发现已经一台主机的微服务应用A已经OOM了,然后约1小时之后,另一个主机的微服务应用A也OOM,通过翻看日志,发现溢出和业务逻辑代码没有特别明显的关系。第二天上午,备机房的两个应用A也相继触发了OOM,下图是测试环境模拟出来的OOM日志,可以明显看到java.lang.OutOfMemoryError:Java heap space的溢出信息。

这是使用jvisualvm观察应用在测试环境慢慢溢出的内存使用情况,可以看出内存可以回收的越来越少,逐渐上涨,这是非常不健康的回收情况,如下图:

通过jstat工具来查看应用的回收情况,也能看出溢出的情况,如下图:

通过top命令可以看到第进程CPU飙升,通常内存溢出时,都会表现CPU飙升,其实这时CPU都在忙着回收基本无法回收的垃圾,溢出啦。如下图:

通过用mat分析Dump错误信息如下:

根据日志报有OutOfMemory类型错误,分析有存在内存泄露,dump文件的错误类型有dubbo应用包占比过多,还有finalize优先级低于GC的线程处理占用过多内存。仔细检查项目代码,未发现应用程序层面的问题。随后安排进行了较长时间的稳定性测试。生产极可能为多天积累导致的内存泄露。

测试时为了较快复现问题,在服务端设置线程sleep三秒钟,并加大并发量,缩小虚拟机内存。测试工具为loadrunner,监控工具为jvisualvm、jconsole。

测试结果如下:

两个多小时的持续压测,系统老年代的堆栈一直居高,不能正常回收。

jconsole观测如下图:

运行12个多小时后,内存耗尽,堆内存不能正常回收,检查dump等文件,测试环境的日志报错和生产也完全一致,算是把问题重现了。使用jvisualvm观察如下图:

老年代的内存不能释放被回收,后仅对应用A进行性能测试,不通过dubbo连接微服务,一切正常,老年代堆栈可以正常回收。判断极可能为dubbo相关包的问题。同时,通过mat对dump日志分析,也指向dubbo相关jar包,后搜索相关类,和排查dubbo相关包源码,发现spring引入的dubbo关联包为快照版,包名为spring-xxx-dubbo-xxx-SNAPSHOT.jar,该包为非正式版,通过排除相关类,发现有做了一些统计操作和通讯加上了同步锁(synchronized),资源释放慢,很容易导致老年代内存回收失败。

替换为产品之前确认的dubbo-spring-xxxx.jar正式包,同样是加大并发,缩小内存来压测,测试情况如下:

运行3个小时后,堆栈内存正常回收一次

运行7个小时后,堆栈内存正常回收

运行20个小时后,堆栈运行依然正常稳定,看到规律的回收情况,心里的石头算是落地了。

后记,在实际的生产中,内存溢出问题的原因很多,但是大都因为使用对象不当。例如查库一次性获取数据量大导致大对象产生,这时溢出异常日志往往上面是有明显的业务逻辑代码的操作。这类OOM比较好处理,通过优化业务逻辑代码即可。像本次非逻辑代码相关的,还需要借助mat来分析dump,并逐步缩小范围排除来找出问题根因。

相关推荐
夕除21 分钟前
js--15
java·jvm·spring
4311媒体网3 小时前
C语言操作符全解析 C语言操作符详解
java·c语言·jvm
猫头虎12 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
wgslucky13 小时前
jdk17 配置jvm参数中gc的日志及控制日志数量和大小
jvm·gc·-xlog
痴儿哈哈18 小时前
自动化机器学习(AutoML)库TPOT使用指南
jvm·数据库·python
野犬寒鸦1 天前
从零起步学习并发编程 || 第七章:ThreadLocal深层解析及常见问题解决方案
java·服务器·开发语言·jvm·后端·学习
闻哥1 天前
Kafka高吞吐量核心揭秘:四大技术架构深度解析
java·jvm·面试·kafka·rabbitmq·springboot
星辰_mya1 天前
Elasticsearch线上问题之慢查询
java·开发语言·jvm
蓝帆傲亦1 天前
代码革命!我用Claude Code 3个月完成1年工作量,这些实战经验全给你
jvm·数据库·oracle
Codiggerworld2 天前
JVM内存模型——你的对象住在哪里?
jvm