詳解Java程序?qū)е翪PU打滿如何排查
一、哪些情況會導(dǎo)致Java程序CPU打滿?
在開始排查前,我們先了解可能導(dǎo)致Java應(yīng)用CPU使用率飆升的常見原因:
死循環(huán)或無限遞歸
- 特征:單個線程持續(xù)占用一個CPU核心,CPU使用率接近100%。
- 場景:代碼邏輯錯誤、條件判斷失誤導(dǎo)致循環(huán)無法退出。
高復(fù)雜度算法處理大量數(shù)據(jù)
- 特征:CPU使用率隨數(shù)據(jù)量線性或指數(shù)級上升。
- 場景:如未優(yōu)化的排序、遞歸遍歷、大數(shù)據(jù)計算等。
線程阻塞與喚醒風(fēng)暴(Lock Contention)
- 特征:大量線程在
RUNNABLE和BLOCKED狀態(tài)間頻繁切換,引發(fā)大量上下文切換。 - 場景:鎖競爭激烈、synchronized或ReentrantLock使用不當(dāng)。
- 特征:大量線程在
線程池配置不合理
- 特征:線程數(shù)過多,導(dǎo)致上下文切換開銷巨大,CPU資源被調(diào)度消耗。
- 場景:核心線程數(shù)、最大線程數(shù)設(shè)置過大,或隊列策略不合理。
頻繁GC(垃圾回收)
- 特征:GC線程占用大量CPU,尤其是Full GC頻繁觸發(fā)。
- 場景:內(nèi)存泄漏、堆內(nèi)存設(shè)置不合理、對象創(chuàng)建過快。
此外,還有如正則表達式回溯、JNI調(diào)用、頻繁反射等也可能導(dǎo)致CPU飆升。
二、環(huán)境準備:模擬CPU打滿場景
為了演示排查過程,我們先編寫一段模擬CPU打滿的Java代碼:
// Cpu100DemoApplication.java
public class Cpu100DemoApplication {
public static void main(String[] args) {
int coreCount = Runtime.getRuntime().availableProcessors();
System.out.println("CPU核心數(shù):" + coreCount);
for (int i = 0; i < coreCount; i++) {
new Thread(() -> {
while (true) {
// 死循環(huán)空轉(zhuǎn),持續(xù)占用CPU
}
}, "cpu-eater-thread-" + i).start();
}
}
}
操作步驟:
# 1. 編譯 javac Cpu100DemoApplication.java # 2. 創(chuàng)建 MANIFEST mkdir -p META-INF cat > META-INF/MANIFEST.MF << 'EOF' Manifest-Version: 1.0 Main-Class: Cpu100DemoApplication Created-By: lyc EOF # 3. 打包 jar cvfm Cpu100Demo.jar META-INF/MANIFEST.MF Cpu100DemoApplication.class # 4. 運行測試 java -jar Cpu100Demo.jar
此時,程序會為每個CPU核心創(chuàng)建一個死循環(huán)線程,模擬CPU打滿場景。
三、方式一:使用top + jstack定位問題
這是最基礎(chǔ)、最經(jīng)典的排查方式,適用于所有Linux環(huán)境,無需額外工具。
步驟1:使用top查看進程CPU使用情況
top
觀察輸出,重點關(guān)注:
- PID:進程ID
- %CPU:CPU使用率(多核系統(tǒng)下可能超過100%)
- %MEM:內(nèi)存使用率
例如:
PID %CPU %MEM COMMAND 21423 194.0 10.2 java -jar cpu-demo.jar
說明:我的服務(wù)器是2核,因此CPU最大使用率為200%。194%的使用率已接近打滿,說明進程 21423 是問題源頭。
步驟2:查看進程中各線程的CPU使用情況
使用 top -H 查看線程級CPU占用:
top -H -p 21423
輸出中會列出該進程的所有線程,找到CPU使用率最高的幾個線程,例如:
PID %CPU COMMAND 21444 97.0 java 21445 96.5 java
這兩個線程ID(21444、21445)極有可能是問題線程。
步驟3:將線程ID轉(zhuǎn)換為16進制
JVM線程堆棧中的線程ID(nid)是16進制的,需轉(zhuǎn)換:
printf '%x\n' 14898 # 輸出:3a32 printf '%x\n' 21445 # 輸出:53c5
記住這兩個值:3a32 和 53c5。
步驟4:使用jstack查看線程堆棧
執(zhí)行命令獲取進程的線程快照:
jstack -l 21423 > jstack.log
打開日志文件,搜索 cpu-eater
"cpu-eater-thread-0" #14 prio=5 os_prio=0 cpu=390259.69ms elapsed=395.52s tid=0x0000558fec247c50 nid=0x53c2 runnable [0x00007f2288f78000]
java.lang.Thread.State: RUNNABLE
at Cpu100DemoApplication.lambda$main$0(Cpu100DemoApplication.java:8)
at Cpu100DemoApplication$$Lambda$1/0x0000000800c00a08.run(Unknown Source)
at java.lang.Thread.run(java.base@17.0.0.1/Thread.java:833)
定位成功! 問題出現(xiàn)在 Cpu100DemoApplication.java 死循環(huán)代碼。
四、方式二:使用 Arthas 快速診斷
Arthas 是阿里巴巴開源的Java診斷工具,被譽為“Java程序員的瑞士軍刀”。它無需修改代碼,即可實時監(jiān)控、診斷線上應(yīng)用。
1. 安裝與啟動
# 下載 curl -O https://arthas.aliyun.com/arthas-boot.jar # 啟動 java -jar arthas-boot.jar
啟動后,Arthas會列出當(dāng)前機器上所有Java進程:
* [1]: 3439 org.elasticsearch.bootstrap.Elasticsearch [2]: 21423 Cpu100Demo.jar
輸入進程ID(如 2),回車進入監(jiān)控界面。
2. 使用thread命令定位高CPU線程
# 查看CPU使用率最高的5個線程 thread -n 5
輸出示例:
"cpu-eater-thread-3" Id=17 cpuUsage=99.95% deltaTime=210ms time=929934ms RUNNABLE
at app//Cpu100DemoApplication.lambda$main$0(Cpu100DemoApplication.java:8)
at app//Cpu100DemoApplication$$Lambda$1/0x0000000800c00a08.run(Unknown Source)
at java.base@17.0.0.1/java.lang.Thread.run(Thread.java:833)
"cpu-eater-thread-4" Id=18 cpuUsage=99.89% deltaTime=210ms time=930931ms RUNNABLE
at app//Cpu100DemoApplication.lambda$main$0(Cpu100DemoApplication.java:8)
at app//Cpu100DemoApplication$$Lambda$1/0x0000000800c00a08.run(Unknown Source)
at java.base@17.0.0.1/java.lang.Thread.run(Thread.java:833)
無需轉(zhuǎn)換進制,直接顯示CPU使用率和代碼位置,定位問題更直觀、更高效!
3. Arthas的其他強大功能(擴展)
watch:監(jiān)控方法的入?yún)?、返回值、異?/li>trace:追蹤方法調(diào)用鏈,分析性能瓶頸stack:查看指定方法的調(diào)用棧dashboard:實時監(jiān)控系統(tǒng)、JVM、線程、內(nèi)存等狀態(tài)jvm:查看JVM信息ognl:執(zhí)行任意OGNL表達式,調(diào)用對象方法
Arthas讓線上問題排查從“盲人摸象”變?yōu)?ldquo;全局掌控”。
五、總結(jié)與建議
| 方法 | 優(yōu)點 | 缺點 | 推薦指數(shù) |
|---|---|---|---|
| top + jstack | 原生工具,無需安裝,通用性強 | 操作繁瑣,需手動轉(zhuǎn)換進制,信息不夠直觀 | ???? |
| Arthas | 功能強大,定位迅速,交互友好,支持熱修復(fù) | 需額外安裝,有一定學(xué)習(xí)成本 | ????? |
最佳實踐建議:
- 日常開發(fā)中優(yōu)先使用Arthas,提升排查效率。
- 掌握top + jstack作為基礎(chǔ)技能,應(yīng)對無外網(wǎng)或受限環(huán)境。
- 定期監(jiān)控系統(tǒng)指標(biāo),結(jié)合Prometheus + Grafana實現(xiàn)告警。
- 優(yōu)化代碼邏輯,避免死循環(huán)、高復(fù)雜度算法、過度創(chuàng)建線程。
- 合理配置JVM參數(shù)和線程池,避免資源浪費。
結(jié)語:
CPU打滿并不可怕,關(guān)鍵在于快速定位、精準修復(fù)。掌握本文介紹的兩種排查方式,你將能在面對線上性能問題時從容不迫,成為團隊中不可或缺的技術(shù)骨干。
到此這篇關(guān)于詳解Java程序?qū)е翪PU打滿如何排查的文章就介紹到這了,更多相關(guān)Java CPU打滿內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot集成分頁插件PageHelper的配置和使用過程
這篇文章主要介紹了SpringBoot集成分頁插件PageHelper的配置和使用過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04
Java對數(shù)組實現(xiàn)選擇排序算法的實例詳解
這篇文章主要介紹了Java對數(shù)組實現(xiàn)選擇排序算法的實例,選擇排序的比較次數(shù)為 O(N^2)而交換數(shù)為O(N),需要的朋友可以參考下2016-04-04
使用IntelliJ IDEA 進行代碼對比的方法(兩種方法)
這篇文章給大家?guī)砹藘煞NIntelliJ IDEA 進行代碼對比的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2018-01-01
java實現(xiàn)批量導(dǎo)入Excel表格數(shù)據(jù)到數(shù)據(jù)庫
這篇文章主要為大家詳細介紹了java實現(xiàn)批量導(dǎo)入Excel表格數(shù)據(jù)到數(shù)據(jù)庫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-08-08

