在生產(chǎn)環(huán)境中,有時(shí)會(huì)遇到一些CPU占用過(guò)高,一直下不去的場(chǎng)景。出現(xiàn)這種情況,可能會(huì)導(dǎo)致服務(wù)對(duì)外中斷,服務(wù)器超負(fù)荷運(yùn)行影響硬件壽命。這篇文章從實(shí)踐出發(fā),一步一步地分析如何使用 ?top? ??和? ?jstack? ?命令快速定位問(wèn)題代碼位置。
一、top命令
? top ? (table of processes) is a ? ?task manager? ?? program, found in many ? ?Unix-like? ? operating systems, that displays information about CPU and memory utilization.
? ?維基百科? ??解釋到,top (進(jìn)程表)是一個(gè)任務(wù)管理器程序,可以在許多類(lèi) ? ?unix? ?操作系統(tǒng)中找到,它顯示有關(guān) CPU 和內(nèi)存使用情況的信息。
同時(shí),top命令是linux下常用的性能分析工具,能夠?qū)崟r(shí)顯示系統(tǒng)中各個(gè)進(jìn)程的資源占用狀況,類(lèi)似于windows的任務(wù)管理器。比較準(zhǔn)確的說(shuō),top命令提供了實(shí)時(shí)的對(duì)系統(tǒng)處理器的狀態(tài)監(jiān)視.它將顯示系統(tǒng)中CPU最“敏感”的任務(wù)列表.該命令可以按? CPU使用 ?、? 內(nèi)存使用 ?和? 執(zhí)行時(shí)間 ?對(duì)任務(wù)進(jìn)行排序。
上圖是一個(gè)? ?centos 7? ? 系統(tǒng),執(zhí)行top命令后瞬時(shí)的展示信息。
- 第一行描述系統(tǒng)的信息,包括開(kāi)機(jī)時(shí)間、用戶(hù)登錄、CPU整體負(fù)載
- 第二行描述任務(wù)總體執(zhí)行情況,即各類(lèi)進(jìn)程占比情況
- 第三行描述CPU總體信息,比如空閑占比等
- 第四行描述物理內(nèi)存占比情況
- 第五行描述交換區(qū)占比情況
- 動(dòng)態(tài)的列表描述的詳細(xì)進(jìn)程各項(xiàng)信息,比如? PID ? 進(jìn)程ID, ? %CPU ? 進(jìn)程占用CPU的使用率,? TIME+ ? 該進(jìn)程啟動(dòng)后占用的總的CPU時(shí)間,即占用CPU使用時(shí)間的累加值。
一般linux系統(tǒng)會(huì)自帶top命令,還有另外一個(gè)命令行工具 ? ?htop? ?,有時(shí)需要自行安裝,它與傳統(tǒng)的 top 命令功能一樣,但它有更加強(qiáng)大的功能及能顯示更多的信息。
二、 jstack命令
? jstack ? prints JAVA stack traces of Java threads for a given Java process or core file or a remote debug server。
? ?oracle官網(wǎng)? ?解釋到,Jstack 打印給定 Java 進(jìn)程或核心文件或遠(yuǎn)程調(diào)試服務(wù)器的 Java 線程的 Java 堆棧跟蹤。
對(duì)于每個(gè) Java 框架,使用jstack指令將打印完整的類(lèi)名、方法名、“ bci”(字節(jié)碼索引)和行號(hào)(如果可用)。使用-m 選項(xiàng),jstack 將打印所有線程的 Java 和本機(jī)幀以及“ pc”(程序計(jì)數(shù)器)。
用法很簡(jiǎn)單,如下圖。
一般說(shuō),我們會(huì)先使用? ?jps? ?命令顯示當(dāng)前所有java進(jìn)程pid的命令。
$jps
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(
img-XZxLXjOe-1628255428026)(https://upload-images.jianshu.io/upload_images/26464854-8cd522cd811debd3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
然后再執(zhí)行對(duì)執(zhí)行進(jìn)程ID 執(zhí)行 jstack。
$ jstack 15673
如果明確自己的Java進(jìn)程ID,當(dāng)然直接使用 jstack指令即可。
三、實(shí)戰(zhàn)
3.1 實(shí)戰(zhàn)之前準(zhǔn)備
先寫(xiě)一個(gè)簡(jiǎn)單的榨干CPU的例子? ?CpuDryOutExample.java? ?,內(nèi)容相當(dāng)?shù)膃asy,就是在無(wú)中斷條件的循環(huán)語(yǔ)句中,不斷地執(zhí)行打印內(nèi)容操作。
?寫(xiě)個(gè)例子?
public class CpuDryOutExample {
public static void dryOut() {
while (true) {
System.out.println("dry out utilization rate of cpu");
//do something
}
}
public static void main(String[] args) {
CpuDryOutExample.dryOut();
}
}
?編譯?
$javac CpuDryOutExample.java
輸出? ?CpuDryOutExample.class? ?,注意最好去掉java文件里面的package,否則編譯過(guò)程中很有可能會(huì)報(bào)找不到main方法,當(dāng)然你編譯時(shí)特殊處理也行。
?運(yùn)行?
將? ?CpuDryOutExample.class? ??上傳到? ?Centos 7? ? 服務(wù)器上,該服務(wù)器已經(jīng)預(yù)先配置好了Java運(yùn)行環(huán)境(JRE),然后執(zhí)行下面命令運(yùn)行程序。
$java CpuDryOutExample
此時(shí)屏幕瘋狂輸入? dry out utilization rate ?
3.2 定位問(wèn)題
定位的核心是三個(gè)步驟
- 找到CPU占用率比較高的進(jìn)程ID
- 在指定進(jìn)程ID的進(jìn)程中尋找進(jìn)程CPU占用率比較高的線程ID
- 通過(guò)線程ID去搜索打印出的進(jìn)程堆棧日志,定位到具體的問(wèn)題
?找到CPU占用率比較高的進(jìn)程ID?
在終端上輸入? ?top? ?命令
$top
可以明顯的看出PID為9573的進(jìn)程CPU占用率最高,我們使用? ?htop? ?命令會(huì)更加直觀一點(diǎn)。
?查看進(jìn)程里面線程運(yùn)行的信息?
我們都知道線程是處理器任務(wù)調(diào)度和執(zhí)行的基本單位,一個(gè)進(jìn)程下是是包含多個(gè)線程。進(jìn)程粒度還是過(guò)大,不便于我們定位到具體的代碼位置,我們需要找到具體是哪個(gè)線程過(guò)度使用CPU。
我們還是使用? ?top? ?命令。
//-H 顯示線程信息,-p指定pid jstack 線程ID
#$top -Hp 9573
圖上可以真正的看出,使CPU使用率暴漲的罪魁禍?zhǔn)资蔷€程 9574。當(dāng)然使用? ?htop? ?我們也能很快的定位到具體線程。
?分析過(guò)濾定位問(wèn)題?
因?yàn)榫€程ID在堆棧日志中是以16進(jìn)制呈現(xiàn),我們先進(jìn)行進(jìn)制轉(zhuǎn)換。
$printf %s 9574
然后打印堆棧日志到臨時(shí)文件1.txt
# 注意是進(jìn)程ID
$jstack 9573 > 1.txt
然后在文件中搜索線程所在位置
//在文件中搜索過(guò)濾并打印30條數(shù)據(jù)
$cat 1.txt | grep -A 30 2566
可以清楚的看到,紅框位置就是具體問(wèn)題代碼。
四、總結(jié)
除了使用? ?top/htop + jstack? ?? 命令的方式,我們也可以使用? ?JMC? ??快速定位CPU占用率過(guò)高的問(wèn)題,雖然? ?JMC? ?確實(shí)比前者更加簡(jiǎn)單高效,但是只能使用JMX實(shí)現(xiàn)遠(yuǎn)程連接,如果部署的服務(wù)沒(méi)有啟用JMX是用不上這個(gè)工具。
那么回到問(wèn)題的根源,什么場(chǎng)景會(huì)導(dǎo)致CPU占用率過(guò)高呢?
序列化和反序列(使用合理的類(lèi)庫(kù))
正則表達(dá)式(回溯導(dǎo)致,避免回溯)
頻繁GC,GC線程頻發(fā)執(zhí)行垃圾回收算法(降低GC頻率)
頻繁 的線程上下文切換(降低切換的頻率,根據(jù)業(yè)務(wù)合理建立線程池)
無(wú)限while循環(huán)(盡量有限循環(huán),即設(shè)置中斷條件,讓循環(huán)執(zhí)行的慢一點(diǎn),即? ?Thead.yield? ?)
頻繁創(chuàng)建新對(duì)象(合理使用單例)
原文鏈接:
https://blog.51cto.com/u_15477630/5051043?utm_source=tuicool&utm_medium=referral