您的位置:首页 > 编程语言 > Java开发

java 一次CPU占用过高问题的排查及解决

2018-08-09 15:01 1101 查看

最近一段时间  某台服务器上的一个应用总是隔一段时间就自己挂掉      用top看了看  从重新部署应用开始没有多长时间CPU占用上升得很快  

排查步骤

1.使用top 定位到占用CPU高的进程PID

  top 

2.通过ps aux | grep PID命令

  获取线程信息,并找到占用CPU高的线程

  ps -mp pid -o THREAD,tid,time | sort -rn 

3.将需要的线程ID转换为16进制格式

  printf "%x\n" tid

4.打印线程的堆栈信息  到了这一步具体看堆栈的日志来定位问题了

  jstack pid |grep tid -A 30

----------------------------------------------------------------------------  华丽的分割线  ------------------------------------------------------------------------------------------------------------------

 

top     可以看出PID  733进程 的占用CPU  172%

    查找进程733下的线程  可以看到TID 线程775占用了96%且持有了很长时间  其实到这一步基本上能猜测到应该是    肯定是那段代码发生了死循环

ps -mp 733 -o THREAD,tid,time | sort -rn

 

线程ID转换为16进制格式

printf "%x\n" 775

 

 查看java  的堆栈信息

jstack 733 |grep 307 -A 30

 

 

 

 

显然是 SmsQueueServiceImpl 中的produceMissSms   和 consumeMissSms  方法有问题

一下为精简的部分代码

/**
* Created by dongxc on 2015/7/7. 通知消息队列
*/
@Service("smsQueueService")
public class SmsQueueServiceImpl {
// 生产异常队列方法
public void produceMissSms(SmsLogDo smsLogDo) {
/*
* try{ String key = EnumRedisPrefix.SMS_QUEUE_MISS_DEAL.getValue(); boolean result = redisService.lpush(key,
* smsLogDo, 0); if(result==false){ logger.error("通知消息异常队列生产消息返回失败!"+smsLogDo.getId()); } }catch(Exception e){
* logger.error("通知消息异常队列生产消息失败!", e); }
*/
}

// 消费异常队列方法
public SmsLogDo consumeMissSms() {
try {
String destKey = EnumRedisPrefix.SMS_QUEUE_MISS_DEAL.getValue();
SmsLogDo smsLogDo = new SmsLogDo();
Object obj = null;
if (obj == null) {
return null;
} else {
smsLogDo = (SmsLogDo) obj;
}
return smsLogDo;
} catch (Exception e) {
logger.error("通知消息队列消费方法失败!", e);
return null;
}
}
}

从很有年代感的垃圾代码来看  这两个方法并没有什么问题  继续往调用这两个方法的上层排查

 

/**
* Created by dongxc on 2015/7/7.
* 消息通知监控线程
*/
@Service("smsMonitorComsumer")
public class SmsMonitorComsumerImpl {

@Autowired
private SmsQueueServiceImpl smsQueueService;

//取队列里的任务消费
@Transactional(propagation= Propagation.NOT_SUPPORTED)
public void run() {

while (true) {
try {
SmsLogDo smsLogDo = smsQueueService.consumeMissSms();
Boolean result = false;
if(smsLogDo!=null){
long diff = (new Date()).getTime() - smsLogDo.getSendtime().getTime() ;
long min  = diff%(1000*24*60*60)%(1000*60*60)/(1000*60);//计算差多少分钟
if(min>5){
result = true;
}
}
if(result){
smsQueueService.produceSms(smsLogDo);
}else{
smsQueueService.produceMissSms(smsLogDo);
}
} catch (Exception ex) {
try{
Thread.sleep(3000);
}catch(Exception e){
//logger.error("发送站内信息短信时线程执行失败2!", e);
}
}
}

}
}

很显然  这里有一个while(true)  无数个草泥马策马奔腾           ps:垃圾代码看多了, 我已经不愤怒了. 

 基本定位到问题了      while里面完全是没有用的代码

继续往上层看谁来调用

/**
* Created by dongxc on 2015/7/7.
* 通知消息队列
*/
@Service("smsLogRunThread")
public class SmsLogRunThreadImpl {
public int flag;
@Autowired
private SmsLogConsumerImpl smsLogConsumer;
@Autowired
private SmsMonitorComsumerImpl smsMonitorComsumer;

@PostConstruct
public void init() {

if(ip!=""&&host!=""&&ip.equals(host)){
Thread thread = new Thread(){
public void run() {
smsLogConsumer.run();
}
};
thread.start();
Thread thread1 = new Thread(){
public void run() {
smsMonitorComsumer.run();
}
};
thread1.start();
}

}
}

在应用一启动的时候   spring初始化的就会执行这一段处理丢失消息的代码   然后这段死循环代码  没有任何作用    

解决方法   即   注释掉whlie(true)这一段代码

 

 重新部署后 cpu占用就很正常了

 

案例一下,其实之前也遇到过CPU占用很高的问题,  但是那次是  频繁的GC导致的

其实排查问题 的过程中也是在不断的学习的过程 ! 先打个鸡血,我要继续搬砖了

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: