1 場景
通過linux的top命令
和jdk的jstack
命令來排查當前系統(tǒng)CPU占用最多的線程跑揉。
2 步驟
主要步驟如下圖:
2.1 測試代碼
SpringBoot中寫一個死循環(huán)
// package com.sa.jvm.admin.controller;
/**
* 死循環(huán)測試
*/
@Controller
@RequestMapping("/infiniteLoop")
public class PowerController {
@RequestMapping("test")
public String test() {
while (1 == 1) {
System.out.println(new Random().nextInt(1000));
}
}
}
當前訪問此請求路徑(/infiniteLoop/test)時啃勉,CPU將飆升。
發(fā)起請求后随闺,控制臺將無限輸出1000以內(nèi)的隨機數(shù)日川,如下:
http://192.168.0.11:8080/infiniteLoop/test
2.2 排查問題
(1)查找CPU占用最高的進程
linux系統(tǒng)中輸入如下命令,查看CPU占用最高的進程
:
top -c
如圖所示矩乐,PIP為3147
的java進程的CPU占比最高龄句。此處已經(jīng)定位到有問題的進程
PID為3147。
top -c含義:
每隔5秒顯式進程的資源占用情況绰精,并顯示進程的命令行參數(shù)(默認只有進程名)
另外TOP模式下,常用交互命令如下:
命令 | 說明 |
---|---|
P | 以 CPU 占用率大小的順序排列進程列表 |
M | 以內(nèi)存占用率大小的順序排列進程列表 |
(2)查看進程中CPU占比最高的線程
已經(jīng)找到CPU占比最高的進程透葛,ID為3147
笨使,繼續(xù)查找此進程對應的線程信息
。
linux系統(tǒng)中輸入如下命令僚害,查找指定進程中硫椰,CPU占用最高的線程
:
top -H -p 3147
如圖所示,該進程中CPU占比最高的進程的PID為:3168
萨蚕,此處已經(jīng)定位到有問題的線程
PID為3168靶草。
linux中通過如下命令,將線程的PID轉(zhuǎn)為小寫16進制岳遥。
printf '%x\n' 3168
得到16進制的線程ID為:c60
(3)導出進程堆棧信息
此處我們得到有問題的進程為3147
奕翔,有問題的線程ID的16進制為c60
。
先使用jdk的jstack
命令根據(jù)線程PID
導出對應的線程棧信息浩蓉。
jstack 3147
此命令會輸出此java進程中的所有的線程棧
信息派继,我們通過有問題的線程的16進制PID
查找問題線程對應的棧信息宾袜。
可以使用如下命令進行線程棧信息過濾
jstack 3147 | grep c60 -A 50
或j將線程棧信息重定向到文件中,再進行查詢:
jstack 3147 > jstack.log
得到查詢結(jié)果如下:
如圖所示驾窟,此CPU占用主要為輸出打印內(nèi)容導致的CPU占用庆猫,有問題的代碼為:
at com.sa.jvm.admin.controller.PowerController.test(PowerController.java:18)
我們查看源碼,進行問題定位:
有問題的代碼第18行如下绅络,此時已經(jīng)定位到問題的代碼月培,和我們模擬的死循環(huán)代碼一致:
3 服務器CPU使用率高的情況
服務器CPU使用率高的情況,除了以上的業(yè)務代碼外恩急。此處對可能出現(xiàn)的原因進行分析杉畜。
3.1 CAS
使用AtomicInteger
、CountdownLatch
等基于CAS
的相關類時或者自己實現(xiàn)CAS時假栓,當值修改失敗
時寻行,會一直高速循環(huán)
。
解決方案:
(1)限制重試次數(shù)
(2)間隔一段時間后重試
3.2 程序死循環(huán)
如本文的案例匾荆,當業(yè)務代碼出現(xiàn)死循環(huán)無法跳出
的情況時拌蜘,會長時間大量占用CPU。
解決方案:
在代碼級別進行控制牙丽,保證代碼質(zhì)量和健壯性简卧,流程代碼需有退出循環(huán)的條件
。
3.3 tomcat并發(fā)量太高
tomcat默認線程池數(shù)量200烤芦,一般不會更改為太高举娩,大多數(shù)的請求都是快速響應
。
并發(fā)請求數(shù)暴增构罗,超過tomcat的負載能力
后铜涉,web容器中會堆積大量的請求,會占用過多的CPU資源導致響應慢和卡頓遂唧。
解決方案:
(1)限流芙代、進行請求數(shù)量的控制
(2)負載均衡
(3)處理相應慢的請求業(yè)務代碼......
3.4 服務器被攻擊
當前服務器的端口被攻擊
后(如redis的6379端口)。
如何排查:
查看服務器網(wǎng)路的帶寬情況和非法程序
解決方案:
(1)更改程序的通用端口為其他端口盖彭,防止被攻擊
(2)運行環(huán)境纹烹,盡量使用同一的局域網(wǎng),只對外部暴露可供訪問的程序端口召边。如使用阿里云等環(huán)境铺呵,需盡量使用同一的云環(huán)境,環(huán)境中的服務可通過局域網(wǎng)的方式訪問隧熙。
3.5 java虛擬機GC頻繁
當java虛擬機頻繁GC
時片挂,根據(jù)本文的方法,查找到的CPU占用高的線程是JVM的GC線程
,沒有對應的業(yè)務代碼宴卖,無法定位業(yè)務代碼
滋将。
解決方案:
(1)加大堆內(nèi)存(臨時解決
)
(2)生成堆dump,通過MAT或jprofile排查大對象產(chǎn)生的代碼症昏。一般生產(chǎn)環(huán)境随闽,不會允許進行堆dump
,此種情況肝谭,可以考慮使用本文的方法掘宪,查找僅CPU占用率前幾的線程棧信息
或者CPU執(zhí)行時間前幾的線程棧信息
。