java 一次CPU占用過高問題的排查及解決


最近一段時間  某台服務器上的一個應用總是隔一段時間就自己掛掉      用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導致的

其實排查問題 的過程中也是在不斷的學習的過程 ! 先打個雞血,我要繼續搬磚了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM