赞
踩
CPU飙升问题定位的一般步骤是:
如果是空循环,或者空自旋。
处理方式:可以使用Thread.sleep或者加锁,让线程适当的阻塞。
在循环的代码逻辑中,创建大量的新对象导致频繁GC。比如,从mysql查出了大量的数据,比如100W以上等等。
处理方式:可以减少对象的创建数量,或者,可以考虑使用 对象池。
其他的一些造成CPU飙升的场景,比如 selector空轮训导致CPU飙升 。
处理方式:参考Netty源码,无效的事件查询到了一定的次数,进行 selector 重建。
登录服务器,执行top命令,查看CPU占用情况,找到进程的pid
top
很容易发现,PID为29706的java进程的CPU飙升到700%多,且一直降不下来,很显然出现了问题。
使用 top -Hp <pid>
命令(为Java进程的id号)查看该Java进程内所有线程的资源占用情况
tip: 按shft+p按照cpu占用进行排序,按shift+m按照内存占用进行排序
top -Hp 29706
不难发现,多个线程的CPU占用达到了90%多。我们挑选线程号为30309的线程继续分析
可以使用 printf “%x\n” 命令(tid指线程的id号)将以上10进制的线程号转换为16进制
printf "%x\n"
转换后的结果分别为7665,由于导出的线程快照中线程的nid是16进制的,而16进制以0x开头,所以对应的16进制的线程号nid为0x7665
通过使用jdk自带命令jstack获取该java进程的线程快照并输入到文件中
注意:open jdk不带该命令|没有注册环境变量的,需要对应的java目录执行jstack命令
jstack -l 进程ID > ./jstack_result.txt
命令(为Java进程的id号)来获取线程快照结果并输入到指定文件
jstack -l 29706 > ./jstack_result.txt
在jstack_result.txt 文件中根据线程好nid搜索对应的线程描述
cat jstack_result.txt |grep -A 100 7665
下面是ImageConverter.run()方法中的部分核心代码。
逻辑说明:
/存储minicap的socket连接返回的数据 (改用消息队列存储读到的流数据) ,设置阻塞队列长度,防止出现内存溢出
//全局变量
private BlockingQueue<byte[]> dataQueue = new LinkedBlockingQueue<byte[]>(100000);
//消费线程
@Override
public void run() {
//long start = System.currentTimeMillis();
while (isRunning) {
//分析这里从LinkedBlockingQueue
if (dataQueue.isEmpty()) {
continue;
}
byte[] buffer = device.getMinicap().dataQueue.poll();
int len = buffer.length;
}
在while循环中,不断读取堵塞队列dataQueue中的数据,如果数据为空,则执行continue进行下一次循环。
如果不为空,则通过poll()方法读取数据,做相关逻辑处理。
初看这段代码好像每什么问题,但是如果dataQueue对象长期为空的话,这里就会一直空循环,导致CPU飙升。
那么如果解决呢?
//取出队列中的头部元素,如果队列为空则调用此方法的线程被阻塞等待,直到有元素能被取出,如果等待过程被中断则抛出InterruptedException
E take() throws InterruptedException;
//取出队列中的头部元素,如果队列为空返回null
E poll();
这两种取值的API,显然take方法更时候这里的场景。
代码修改为:
while (isRunning) {
/* if (device.getMinicap().dataQueue.isEmpty()) {
continue;
}*/
byte[] buffer = new byte[0];
try {
buffer = device.getMinicap().dataQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
……
}
重启项目后,测试发现项目运行稳定,对应项目进程的CPU消耗占比不到10%
本文案例取之网上,公司代码敏感
dumpapp(){ echo "应用dump:=============start========================" is_exist if [ $? -eq 1 ];then echo "${appName} is not running" exit 0 fi echo "${appName} Process ID is : $pid" echo "${appName} Process ID is : $pid" > ${logsPath}/${appName}_${pid}_${caijiTime}.txt #CUP top cupNum的数据写入临时文件 ps p ${pid} -L -o pcpu,pid,tid,time | sort -f -k4 | tail -n -${cupNum} | grep -E ':'> ${logsPath}/temp.txt echo " " echo "CPU TOP ${cupNum} :" echo "CPU TOP ${cupNum} :" >> ${logsPath}/${appName}_${pid}_${caijiTime}.txt cat ${logsPath}/temp.txt cat ${logsPath}/temp.txt >> ${logsPath}/${appName}_${pid}_${caijiTime}.txt echo " " processId=`awk '{print $3}' ${logsPath}/temp.txt` echo "${processId}" > ${logsPath}/temp.txt while read LINE do tempId=`printf "%x" ${LINE}` echo "线程${LINE}对应的十六进制编码是:${tempId}" echo "线程${LINE}对应的十六进制编码是:${tempId}" >> ${logsPath}/${appName}_${pid}_${caijiTime}.txt done < ${logsPath}/temp.txt rm -rf ${logsPath}/${pid}.log rm -rf ${logsPath}/${pid}.hprof ${jdkPath}/jstack -l ${pid} > ${logsPath}/${appName}_${pid}_${caijiTime}.log ${jdkPath}/jmap -dump:format=b,file=${logsPath}/${appName}_${pid}_${caijiTime}.hprof ${pid} echo "Heap dump file Completion" echo " " echo "log文件路径:${logsPath}/${appName}_${pid}_${caijiTime}.log" echo "hprof文件路径:${logsPath}/${appName}_${pid}_${caijiTime}.hprof" echo "log文件路径:${logsPath}/${appName}_${pid}_${caijiTime}.log" >> ${logsPath}/${appName}_${pid}_${caijiTime}.txt echo "hprof文件路径:${logsPath}/${appName}_${pid}_${caijiTime}.hprof" >> ${logsPath}/${appName}_${pid}_${caijiTime}.txt echo "应用dump:=============end========================" }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。