阅读:17815回复:0
Java web项目导致服务器CPU满载的解决方案
最近我的一个部署在centos7上的一个项目出现了CPU达到了1000%+的状态,导致项目崩溃,起初没重视,直接重启项目解决,后面项目运行了几天服务器又报警1000%+了,这个时候我才意识到项目问题的严重性。然后开始查找原因,查看数据库连接池,发现数据库连接已经爆满,超过了最大连接数,review代码,发现很多数据库操作的时候没有释放连接,改代码,重新部署。这个时候项目运行算比较稳定了,但是好景不长,过了一个星期左右CPU又1000%+了。苦恼了。在某一天晚上逛CSDN的时候突然看到一篇关于CPU调试的文章,怎么查看CPU进程的。正好服务器又爆了,已近爆太多次了,幸好项目没正式上线不然客户非吃了我。具体如下:
1、 使用top命令查找最消耗CPU的进程 图片:top.png 发现26324这个Java进程已经占了1297%的CPU,这个是我们的jboss进程没错了,太恐怖了 2、 把进程的栈dump到文件里,以便后面的分析 命令:jstack pid > cpu0414.log 3、 看看26324这个进程里面哪些线程在占用cpu 命令:top -p 26324 -H 图片:2.png 妈哟,16核心的服务器有13个核心被沾满,随便选一个占用高的吧,就你了PID=26327 4、 先把刚才dump出来的log文件拷到本地方便阅读 命令find / -name cpu0414.log 查找文件所在的位置 图片:find.png 在root目录下,直接用winscp拷下来吧 5、 在log里面找到26327这个线程的信息,在Linux中pid是10进制显示的,但是在log文件中是16进制,所以要把26327转成16进制才能找得到相应的线程信息 命令:printf "%0x\n" 26327 图片:print.png 紧接着去log中搜索66d7 图片:log.png 发现66d7线程GC了,线程进入了死循环,而且从这个log可以看到那13条占满CPU的线程都GC死循环了。再翻翻log,看看报什么错 图片:error.png 看到这个地方报JSONArray.fromObject的时候报错,这个地方导致了死循环,我擦,这个也能死循环?代码如下: JSONObject jsonObject = JSONObject.fromObject(preDatas.get("datas").toString()); 使用的json-lib-2.4-jdk15.jar架包的,根据小白猜测,应该是json-lib的内部问题,百度一下《JSONArray.fromObject死循环》,果然是。修改代码,使用GSON进行格式转化,修改代码如下: HashMap<String,Object> jsonMap = new HashMap<String, Object>(); Gson gson = new Gson(); jsonMap = gson.fromJson(preDatas.get("datas").toString(), jsonMap.getClass());重新部署,正常跑,问题解决。 总结:问题的解决应该还是要找根本,不能一厢情愿的认为是某个问题导致的,解决问题的时候还是要对症下药。对于Linux服务器系统,还需要更加深入研究。路还很远!!!! 其他扩展知识: 线程状态图 图片:线程.png 1. 新建(NEW):新创建了一个线程对象。 2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。 3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。 4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种: (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。 (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。 (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。 5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。 其他更加详细的线程知识请移步: https://blog.csdn.net/xingjing1226/article/details/81977129 |
|