前两天有开发同学说,线上某个系统的cpu使用率很高,而且系统无法访问了。拿到服务的信息后登录服务器,使用top -H -p 23915命令查看服务器cpu使用情况,结果如下:
根据上面的结果再去反查jvm的线程栈信息,看具体的的线程在干啥。反查发现该条线程确实是垃圾收集线程(线程dump文件找不到了,就不贴图了),再去看jvm垃圾收集情况:
jstat -gcutil 23915 1000 100
从结果命令执行结果来看,应该是系统内存溢出了,只能是通过dump jvm的内存信息了,命令如下:
jmap 23915 -dump:live, format=b, file=heap.bin
拿到dump结果之后,就需要请出jvm内存分析工具的神器MAT了,用MAT打开dump出来的文件,由于dump文件有点大,所以打开速度很有点慢(如果在打开过程中出现工具报内存溢出的话,请调整MAT工具的内存配置,具体如何改请自行百度)。
工具分析完成后,我们可以直奔主题,查看`Leak Suspects`,大致如下:
从分析结果看有三处可能发生了内存溢出,点击See stacktrace链接,就可以看到线程栈信息。现在以Problem Suspect 3为例,线程栈如下:
跟开发同学沟通,这个调用是用来展现数据详情的,应该只会从数据库中抓取一条记录用来在详情页面展现,按照道理来说不应该会发生内存溢出的。既然是详情页面,只会从数据库中抓取一条数据,那么我们看看到底是什么内容,占用了这么多内存。点击Leak Suspects页面中的某个问题的Details »链接,我们来看看内存中具体的内容。
经过进一步的分析,终于抓到了如下图所示的具体数据内容:
从分析来看,内存中的数据结构是一个Map和List的嵌套结果,而且在内存中大概存在了2.5w左右的记录,而这些记录确实是数据库中的数据。根据之前的线程栈分析,反过来看程序的源码,终于发现了问题所在:
我只是想问,你判断了cbh不为空的情况(cbh是数据主键),cbh为空呢?为空咋办啊?为空是要全表查询嘛?这可是详情页面的查询啊!!!!!开发同学反馈说,如果cbh为空,确实是做了全表查询而当前分析这问题所关联的表也真的恰好有2.5万左右的数据记录。
后来跟开发同学沟通,为啥会出这样的问题,是因为前端页面是多个人做多,每个人往后台传递主键编号的时候起的参数名字不一样,后来有人对后台代码做了重构,虽然也对前端页面的参数名作成了修改,但是由于前端浏览器缓存的原因还是会导后台获取不到cbh的情况。为了兼容前端浏览器缓存的情况,开发同学在后端对前端传递过来的参数做了兼容处理。修改后经过一天的观察,目前现在服务器内存使用很平稳。
PS:在顺路说一个题外话吧,在排查过程中还发现了另外一个问题,在应用中配置了几个定时任务,而应用在生产环境又做了集群部署。这样一来,集群中每个点上的定时任务都会执行,这样会出现并发问题。还有就是,我们在用spring定时任务的时候,一定要注意任务的并发问题。spring定时任务默认情况下是并行执行的,也就是说即使前一个任务没有执行完成的情况下,后一个任务也是可以启动的。这个问题可以通过
配置来解决。
领取专属 10元无门槛券
私享最新 技术干货