记一次OOM的排查
问题:接手了一个项目,现场性能问题颇多,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使用正常。
上一篇: MapReduce--倒排索引
下一篇: 简单计数器的实现_PHP