Linux環境下使用Arthas(阿爾薩斯)定位cpu飆高問題
- 生產環境cpu飆高的原因
- Arthas工具如何排查Linux環境下cpu飆高的問題
- jvisualvm工具如何排查linux環境下cpu飆高的問題(需要做個配置,才能做遠程連接)
- 生產環境下內存泄露產生的原因
- Arthas工具如何排查Linux環境下內存泄露的問題
生產環境cpu飆高的原因
cpu上運行的是線程
產生原因:
- CAS自旋,沒有控制自旋次數(synchronized鎖:就是使用的自旋,但是如果一直沒有獲取到鎖的話,就會將當前線程從運行狀態變為阻塞狀態) CAS修改值內容,但是CAS也有缺點,就是如果CAS修改值失敗的話,會不斷的進行重試,重試的過程一直是運行狀態的,並且重試是通過循環來實現的,所以是非常消耗cpu資源的
- 解決方案,如果我們在使用樂觀鎖的時候,要限制重試的次數,不要一直重試
- 雲服務器上安裝Redis,黑客攻擊6379端口號,注入挖礦程序
- Redis端口不要外網訪問
- 並發量比較大,或者惡意攻擊
- 解決方式:限流
- 服務器端被DDOS攻擊
- 其實攻擊不好處理,一般是使用限流,ip黑名單,圖形驗證碼,防止機器模擬攻擊
- 死循環
- 一定要加上循環結束條件
創建線程過程中,要配置線程名稱:阿里巴巴開發手冊中有相應規定,便於排查是那些業務相關問題
在Windows下排查的話,主要是查看任務管理下的進程,根據進程定位到業務相關,如果是找不到業務相關,則表示你的線程命名不規范
在Linux下,可以使用top -c查看那個進程的cpu占比比較高
在Windows下,可以使用jdk自帶的jvisualvm排查cpu,但是在1.8之后就沒這個組件了,需要單獨下載jvisualvm獨立版下載
如果沒有配置線程名稱,則該線程可能為Thread-n,從而無法定位業務問題,所以在開啟線程的時候,一定要定義線程名稱、
實際上jvisualvm也是可以連接遠程的,但是鏈接遠程比較麻煩
linux下排查cpu占比
新建java文件,如Test04.java
public class Test04 {
public static void main(String[] args) {
new Thread(() -> {
while (true) {
System.out.println("1111111");
}
}, "hahaha").start();
}
}
上傳至linux或者在linux中新建Test04.java,將代碼進行復制
如圖所示
之后通過javac命令對其進行編譯
javac Test04.java
就會得到class文件,之后使用java Test04執行該文件,不要加.class,如果是一個jar包的話,則應該是java -jar 項目.jar
如何排查cpu飆高的問題呢,需要使用到arthas,首先下載
阿里雲下載
curl -O https://arthas.aliyun.com/arthas-boot.jar
如果執行使用以下命令運行的話,會報錯,錯誤原因在於當前服務器沒有一個關於JVM的進程,沒有的話就會報錯,也就是想要使用Arthas的話,必須先要啟動一個關於JVM的進程,因為沒有找到的話,是不能做監控的,所以需要先運行java程序
java -jar arthas-boot.jar
這個時候如果先運行Test04這個class,再次執行Arthas啟動命令,就又會有提示
通過top -c查看進程
因為有時候可能會檢測到多個,這個時候僅僅只有一個[1],所以可以使用1來代替這個進程,之后回車
這個時候就會對這個4501這個進程做監控了,如何查看當前進程的哪個線程占cpu比較高呢,就可以使用以下命令
# 查看當前進程中前3個占用cpu比較高的線程
thread -n 3
一般大型公司都會搭建監控系統,去監控機器,如果cpu飆高,則會發送郵件通知,一般千萬不要超過90%,超過就掛掉了,掛掉就不能排查了
一般是根據一些征兆去提前告警的
如果是docker或者k8s的話,進入容器安裝即可
docker環境
docker exec -it ${containerId} /bin/bash -c "wget https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar"
k8s環境
kubectl exec -it ${pod} --container ${containerId} -- /bin/bash -c "wget https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar"
查看dashboard
dashboard
通過thread命令來獲取到math-game進程的Main Class
# 會打印線程ID 1的棧,通常是main函數的線程
thread 1 | grep 'main('
at demo.MathGame.main(MathGame.java:17)
通過jad來反編譯Main Class
jad demo.MathGame