背景
最近測試服出現了CPU異常高的情況,占用率接近 100%,所以寫篇文章簡單地記錄下碰到這種情況,該如何去定位導致CPU異常的代碼,下文介紹了幾種比較常用的工具。
下文均基於測試代碼。
准備
我們先准備一個測試項目,此處使用的是一個簡單的 springboot 的 web 項目,直接跑去官網初始化一個,地址:
地址,然后寫了段簡單的示例代碼,見下圖。

打包后放到我本地的虛擬機上運行:nohup java -jar cputest-0.0.1-SNAPSHOT.jar &
在虛擬機上進行本地訪問,運行死循環的那段邏輯:curl 127.0.0.1:7777/api/test
此時使用 top -c 查看服務器的 CPU 信息,發現已經使用了 97%,而且是被我們剛才啟動的 java 進程占用的,PID 為 5008。
COMMAND 注釋就是 ‘java -jar cputest-0.0.1-SNAPSHOT.jar’,顯而易見。

下面就介紹幾種簡單的方法來定位有問題的代碼。
jstack 查看堆棧信息
此處原生的方法其實就是使用 jdk 自帶的工具(需要注意的是:Linux自帶的 openJdk 是沒有的)。
這是最基礎並且最有效的方法,因為很多其它的工具其實還是使用的 openJdk 的這一系列命令,只不過封裝得更為友好了而已。
上文已經查出來是5008這個進程有問題。
所以我們就先查看下5008下面是哪些線程在搞鬼:top -Hp 5008

從面板上我們乍一看,發現了三個異常的線程,PID分別為5010、5092、5093。
要使用 jstack 查看他們的堆棧信息,首先要將 PID 轉為 16進制:printf "%x\n" 5010 5092 5093

接下來就可以查看堆棧信息了,下面以5092這個為例(注意:16進制要在最前面添加0x):jstack 5008 | grep '0x13e4' -A10

問題代碼立馬就找到了。
另外,我們也可以將堆棧信息打印下來:jstack 5008 > cputest.dump然后使用 cat 等命令工具進行過濾查看:cat -n cputest.dump | grep -A10 '0x13e4'
GC 信息也是有問題的:jstat -gcutil 5008 2000 5
比如老年代(O)使用率達到了 84%,元數據區(M)使用率達到了 94%,老年代回收次數(FGC)達到了 540 多

Arthas(阿爾薩斯)
上文的方法比較繁瑣,下面就使用目前特別火的工具來排查下問題。
Arthas 是阿里巴巴開源的一個針對 java 程序的線上診斷工具,全平台支持,有命令行,也有web端,功能非常強大,官網:地址。
我們先安裝基礎工具:curl -O https://alibaba.github.io/arthas/arthas-boot.jar
也可以從 gitee 下載:curl -O https://arthas.gitee.io/arthas-boot.jar
運行:java -jar arthas-boot.jar
Arthas 會羅列出當前服務器上面的所有 Java 進程,我們選擇一個我們想要查看的就行,比如此處輸入1即可。另外,首次運行是需要下載一些依賴工具的,如果無法下載,也可以使用全量安裝。此時左下角也已經變成了 [arthas@5008],這個5008 就是我們想要查看的程序繼進程

控制台輸入 dashboard 即可查看:

從上面的面板上,其實很容易發現有兩個異常的線程,ID 分別為 28 和 27,另外還有異常的 GC 信息。
輸入 Ctrl c 退出面板,接下來我們就來為ID為27的這個線程把把脈,輸入 thread 27 查看線程的詳細信息:

問題代碼就這樣被定為了,so cool !
輸入 quite 即可退出 Arthas。
另外,Arthas 的功能十分強大,遠不限於此,比如自帶 jad 反編譯工具等,此處暫不贅述
show-busy-java-threads
下面的這個工具,也是很常用並且很強大的,而且 show-busy-java-threads 只是useful-scripts 工具包中的工具之一。
下載:地址
上傳 bin 文件夾下的 show-busy-java-threads 腳本到服務器上去。
賦予權限:chmod +x show-busy-java-threads
運行:./show-busy-java-threads

從上圖中就看到了異常代碼的出處。
其他命令:
show-busy-java-threads -c 3:3為n,指定顯示最耗cpu使用率前3的線程。show-busy-java-threads -c 3 -p 17376:展示進程17376耗費CPU組多的3個線程。
總結
此文簡單介紹了三種排查服務器CPU負載高的方案,其實使用體驗都還是比較簡單的,但是有兩點是萬變不離其宗的,那就是底層工具的使用和解決問題的流程。
從打印的堆棧信息中我們就可以看出,信息都長的差不多,所以也可以看出,底層所使用的工具都一樣,其實都是 jdk 的 jstack 等工具。
還有,此類問題一般的解決流程是:
查看 CPU 高負載的進程查看具體的線程找到對應進程的堆棧信息在上述的堆棧信息中過濾出對應線程的信息