问题
近期有上线,上线之后的一段时间内,总是cpu使用率突然飙升,并且程序自动重启
生产环境出现几个问题
1. zabbix cpu使用率突然飙升
2. 日志:内存溢出
另外,还有日志还打印了慢sql:耗时几十分钟。但是实际直接在客户端执行该sql,速度很快,因为数据很少,不可能出现慢sql的问题
原因-先说结论
慢sql只是现象,不是原因
首先是因为内存溢出了,导致系统卡死,所以执行sql看起来很慢,但实际根本不是慢sql的原因
cpu突然飙升也只是现象,不是原因
cpu突然飙升的原因是,线程多,且干活的线程多
干活多的原因是,要么cpu密集,要么磁盘io密集
真正的原因就是看到的内存溢出,这才是本质原因,其他都只是现象和结果
因为内存溢出了,系统卡死,导致做什么都很慢很卡,比如慢sql线程
为什么呢?因为内存不够了,就会疯狂gc,cpu都被gc占用了,业务线程啥都干不了,或者很慢,所以才会出现慢sql等现象
包括cpu使用率突然飙升,不是因为业务线程干活飙升,而是因为没用的gc
===
以上是纯理论层面的
具体原因一般还是:
1. 查询数据库数据太多,比如全表查询
我们这次就是这个原因,全表查询,因为业务没有校验非空入参导致的,呵呵
2. 业务代码循环创建太多对象
分析过程
生产环境已经配置jvm参数,内存溢出的时候自动生成dump文件
使用eclipse内存分析工具分析dump文件
主要是分析
1. 定位到哪一行代码导致的内存溢出
通过线程调用栈,可以定位到是哪一行代码
2. 哪个对象占用太大内存
通过对象大小,可以定位哪个对象占用了太多内存
截图
首先是有个对象占用了4个g
然后点击线程调用栈:定位到报错代码行
报错方法
报错代码行
点击查询是哪个对象占用了这么多内存
定位到是哪个对象占用了大量内存
刚好和前面的代码(线程调用栈)也都对应的上
具体原因
分析结果是
1. 全表查询
因为业务代码没有校验入参非空,导致全表查询,总共4g,500多万数据
2. 然后再循环创建临时对象
注意,刚才的全表查询虽然已经成功,但是这个时候实际上已经到了临界点,即循环创建临时对象的时候,随时都可能导致内存溢出,何况是500多数据的循环,呵呵
看代码就知道了
dump文件6g
eclipse内存分析工具的内存默认只有1g,远远不够,所以启动报错,改为10g
-Xmx10240m
总结
1.我定位到报错代码行和大对象之后,发现有同事更新了代码和sql
2.这次更新sql加了非空判断,该同事本身是想优化一下代码
3.但是呢,没想到业务代码(以前其他同事写的)没有校验非空,导致反而出了问题,因为之前如果入参是空字符串,那么sql就是没有查询到数据,但是现在直接查询全表数据,因为Sql加了非空判断