JavaWeb项目导致Linux服务器CPU过高的解决方案

时间:2024-05-19 22:35:08

        最近我部署在centos7上的一个项目出现了CPU达到了100%+的状态,导致项目崩溃,起初没重视,直接重启项目解决,后面项目运行了几天服务器又报警100%+了,这个时候我才意识到项目问题的严重性。然后开始查找原因,查看数据库连接池,发现数据库连接已经爆满,超过了最大连接数,review代码,发现很多数据库操作的时候没有释放连接,改代码,重新部署。这个时候项目运行算比较稳定了,但是好景不长,过了一个星期左右CPU又100%+了。苦恼了。在某一天晚上逛****的时候突然看到一篇关于CPU调试的文章,怎么查看CPU进程的。正好服务器又爆了,已近爆太多次了,幸好项目没正式上线不然客户非吃了我。具体如下:

1、 使用top命令查找最消耗CPU的进程

JavaWeb项目导致Linux服务器CPU过高的解决方案

发现26324这个Java进程已经占了1297%的CPU,这个是我们的jboss进程没错了,太恐怖了

2、 把进程的栈dump到文件里,以便后面的分析
命令:jstack pid > cpu0414.log 
3、 看看26324这个进程里面哪些线程在占用cpu
命令top -p 26324 -H
JavaWeb项目导致Linux服务器CPU过高的解决方案

妈哟,16核心的服务器有13个核心被沾满,随便选一个占用高的吧,就你了PID=26327

4、 先把刚才dump出来的log文件拷到本地方便阅读
命令find / -name cpu0414.log 查找文件所在的位置

JavaWeb项目导致Linux服务器CPU过高的解决方案

在root目录下,直接用winscp拷下来吧
5、 在log里面找到26327这个线程的信息,在Linux中pid是10进制显示的,但是在log文件中是16进制,所以要把26327转成16进制才能找得到相应的线程信息
命令:printf "%0x\n" 26327

JavaWeb项目导致Linux服务器CPU过高的解决方案

紧接着去log中搜索66d7

JavaWeb项目导致Linux服务器CPU过高的解决方案

发现66d7线程GC了,线程进入了死循环,而且从这个log可以看到那13条占满CPU的线程都GC死循环了。再翻翻log,看看报什么错

JavaWeb项目导致Linux服务器CPU过高的解决方案

看到这个地方报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服务器系统,还需要更加深入研究。路还很远!!!!
其他扩展知识:
 
线程状态图

JavaWeb项目导致Linux服务器CPU过高的解决方案

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.****.net/xingjing1226/article/details/81977129

JavaWeb项目导致Linux服务器CPU过高的解决方案

 

 

相关文章