欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

记一次OOM的排查

程序员文章站 2022-04-28 18:11:23
...

问题:接手了一个项目,现场性能问题颇多,top一下,主要体现在java占用内存高,mysql 占用内存,cpu高,wa值高,于是接到了一个任务,优化,见效快的那种,在优化了一些东西后,在研发环境(mysql用的从现场备份回来的数据)测试的时候发现OOM,且必现(PS:公司网络策略很严,图片都上传不了。。。)

问题排查:

1 从报错日志中,发现是在一个方法执行的时候报的错,这个功能是从别的节点同步数据,大概逻辑是每隔一分钟通过http调用获取5000条数据,然后在for里面,一次50条数据插到数据库中,报错在第一步,通过http获取数据

2 通过jstat工具查看JVM的GC情况

# 到jdk的bin目录下
cd /jdk../bin
# 获取gc的执行情况 1S采样一次,共10次
./jstat -gcutil 进程号 1000 10

发现,old去已经占用90% + ,且此时还没有发生full gc,所以怀疑是不是存在内存泄露的情况

3 顺着这个方向,在代码里面做了一些处理,不用的变量做置空处理,重新项目后发现OOM的情况依然存在

4 决定将堆dump出来,观察堆里面到底存了什么东西

#依然在jdk的bin目录下
./jmap -dump:format=b,file=/20200709.hprof 进程号

将文件down到本地,用jdk自带的工具Java VisualVM打开,发现类目录里面最多的第一是char[],第二是String,第三是PreparedStatement$ParseInfo,在char[]上右键,选择在实例视图中显示,跳转以后,选择按大小排序,发现有很多的实例大小达到了4M,总大小达到了1G,选中一个实例,在右下角引用发现是PreparedStatement的ParseInfo进行了引用,怀疑是mybatis的batchInsert操作有什么坑导致了内存泄露,在网上一番搜索后未果。

5 决定获取更多信息,于是使用VisualVM远程到研发环境监控jvm的情况,具体可参考

https://www.cnblogs.com/xifengxiaoma/p/9402497.html

通过观察发现,当执行1中的操作的时候,堆的使用内存增长的很快,但是当达到full gc的触发条件时,是可以通过full gc将内存的使用情况恢复到正常水平,所以觉得这不是内存泄露,而是内存不足导致的OOM,然后继续观察,在某次堆内存持续上升,快要达到临界点的时候发生了OOM,观察项目日志发现此时代码通过http获取了数据

结论:

 初步结论是通过http获取的数据有5000条,每条数据很大,最大的有4M数量还不少,导致堆内存被占用过高,但是通过full gc可以回收掉,所以不是内存泄露,当old区使用快要到达临界点时,又通过http获取了很多的数据,此时内存不够,且没有进行full gc 所以导致了oom

处理:

减少了每次处理的数据量,JVM使用正常。