java實(shí)戰(zhàn)CPU占用過高問題的排查及解決
最近一段時(shí)間 某臺(tái)服務(wù)器上的一個(gè)應(yīng)用總是隔一段時(shí)間就自己掛掉 用top看了看 從重新部署應(yīng)用開始沒有多長(zhǎng)時(shí)間CPU占用上升得很快
排查步驟
1.使用top 定位到占用CPU高的進(jìn)程PID
top
2.通過ps aux | grep PID命令
獲取線程信息,并找到占用CPU高的線程
ps -mp pid -o THREAD,tid,time | sort -rn
3.將需要的線程ID轉(zhuǎn)換為16進(jìn)制格式
printf "%x\n" tid
4.打印線程的堆棧信息 到了這一步具體看堆棧的日志來定位問題了
jstack pid |grep tid -A 30
top 可以看出PID 733進(jìn)程 的占用CPU 172%

查找進(jìn)程733下的線程 可以看到TID 線程775占用了96%且持有了很長(zhǎng)時(shí)間 其實(shí)到這一步基本上能猜測(cè)到應(yīng)該是 肯定是那段代碼發(fā)生了死循環(huán)
ps -mp 733 -o THREAD,tid,time | sort -rn

線程ID轉(zhuǎn)換為16進(jìn)制格式
printf "%x\n" 775

查看java 的堆棧信息
jstack 733 |grep 307 -A 30


顯然是 SmsQueueServiceImpl 中的produceMissSms 和 consumeMissSms 方法有問題
一下為精簡(jiǎn)的部分代碼
/** * Created by dongxc on 2015/7/7. 通知消息隊(duì)列 */
@Service("smsQueueService")
public class SmsQueueServiceImpl {
// 生產(chǎn)異常隊(duì)列方法
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("通知消息異常隊(duì)列生產(chǎn)消息返回失敗!"+smsLogDo.getId()); } }catch(Exception e){
* logger.error("通知消息異常隊(duì)列生產(chǎn)消息失??!", e); }
*/
}
// 消費(fèi)異常隊(duì)列方法
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("通知消息隊(duì)列消費(fèi)方法失敗!", e);
return null;
}
}
}
從很有年代感的垃圾代碼來看 這兩個(gè)方法并沒有什么問題 繼續(xù)往調(diào)用這兩個(gè)方法的上層排查
/**
* Created by dongxc on 2015/7/7.
* 消息通知監(jiān)控線程
*/
@Service("smsMonitorComsumer")
public class SmsMonitorComsumerImpl {
@Autowired
private SmsQueueServiceImpl smsQueueService;
//取隊(duì)列里的任務(wù)消費(fèi)
@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);//計(jì)算差多少分鐘
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("發(fā)送站內(nèi)信息短信時(shí)線程執(zhí)行失敗2!", e);
}
}
}
}
}
很顯然 這里有一個(gè)while(true) 基本定位到問題了 while里面完全是沒有用的代碼

繼續(xù)往上層看誰來調(diào)用
/**
* Created by dongxc on 2015/7/7.
* 通知消息隊(duì)列
*/
@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();
}
}
}
在應(yīng)用一啟動(dòng)的時(shí)候 spring初始化的就會(huì)執(zhí)行這一段處理丟失消息的代碼 然后這段死循環(huán)代碼 沒有任何作用
解決方法 即 注釋掉whlie(true)這一段代碼
案例一下,其實(shí)之前也遇到過CPU占用很高的問題, 但是那次是 頻繁的GC導(dǎo)致的
其實(shí)排查問題 的過程中也是在不斷的學(xué)習(xí)的過程
相關(guān)文章
Java常用API類之Math System tostring用法詳解
System類代表系統(tǒng),系統(tǒng)級(jí)的很多屬性和控制方法都放置在該類的內(nèi)部。該類位于java.lang包,Java 的 Math 包含了用于執(zhí)行基本數(shù)學(xué)運(yùn)算的屬性和方法,如初等指數(shù)、對(duì)數(shù)、平方根和三角函數(shù),toString() 方法用于返回以一個(gè)字符串表示的 Number 對(duì)象值2021-10-10
java實(shí)現(xiàn)ATM取款項(xiàng)目
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)ATM取款項(xiàng)目的實(shí)現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
java調(diào)用微信現(xiàn)金紅包接口的心得與體會(huì)總結(jié)
這篇文章主要介紹了java調(diào)用微信現(xiàn)金紅包接口的心得與體會(huì)總結(jié),有需要的朋友可以了解一下。2016-11-11
mybatis中的mapper.xml使用循環(huán)語句
這篇文章主要介紹了mybatis中的mapper.xml使用循環(huán)語句,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Java中的HashMap和Hashtable區(qū)別解析
這篇文章主要介紹了Java中的HashMap和Hashtable區(qū)別解析,HashMap和Hashtable都實(shí)現(xiàn)了Map接口,但決定用哪一個(gè)之前先要弄清楚它們之間的區(qū)別,主要的區(qū)別有線程安全性、同步和速度,需要的朋友可以參考下2023-11-11

