jvm cpu占用过高实例以及排查方法

起因

在项目现场发现,程序运行的过程中,发现会时不时有cpu占用400%以上的情况,而通过界面很难定位到触发了哪里导致的,不能缩小分析范围。

收集数据

于是使用了以下几种方式采集数据:
1、对jar包配置jmx参数进行暴露,在重启即可使用jdk的jvisualvm.exe进行远程监控
2、下载阿里的arthas进行监控。
以上两种方式的使用方法就不多说了,请大家自行百度。

分析

一般对于jvm问题,不外乎就两种表现现象,一种就是堆内存占太多没有被释放,一种就是cpu占用过高。
堆内存太多导致堆溢出的原因有很多种,大概就是资源没释放,存在内存的数据量过大等问题导致。而cpu过高一般是程序在执行某段代码执行过长,且一直在计算,要么进入了死循环,要么计算的量太大,导致占用cpu过高。
当然还有死锁的现象,不过这种得看具体线程的状态。

先从大点分析,cpu过高,应该数据计算类问题,那么就得看是那个线程占用了cpu,通过arthas的dashboard查看某个线程后,使用thread 线程号,即可看出该线程运行的代码到哪一步,查看后发现是停在了某个for循环代码中,于是检查那个for循环,发现for(int i=1;i<pages;i++) 这个pages的值是由int pages=(int)Math.ceil((double)total/(double)pageSize)来的,而当pageSize=0时,就会出现int的最大整数2147483647,这个数字使得在for循环转了半天。
那为什么pageSize会等于0呢,是因为前端传入了pageSize为0,而后端没有判断如果为0则给默认值的情况,所以这块就直接过了,那继续问下去,为什么前端会传0呢,按道理一般写法会给分页一个默认值的,了解到后是因为前端想拿所有的数据,而之前对接时想拿所有数据就是这样传的,因为pagehelper的分页如果接收pageSize和pageNum为0的情况下,会查所有记录。那为啥这次想获取所有数据又是采用了分页的做法来获取呢,不搞分页不就行了吗?后面得知是后端这么设计,但前端发现后没提出异议,直接就这么做了,不过与本文就没太大关系了。

结论

在修改了接口后,线上的cpu占用过高问题就结束了,本次只用了arthas的一部分功能,实际上很多功能还没能用到,看官方介绍说明还是很有用处的,有需要的同学可以自己模拟错误情况试一下。


版权声明:本文为lyd135364原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。