在上一篇《jvm調優的幾種場景》中介紹了幾種常見的jvm方面調優的場景,用的都是jdk自帶的小工具,比如jps、jmap、jstack等。用這些自帶的工具排查問題時最大的痛點就是過程比較麻煩,就好比如排查cpu占用率過高的問題,就要top->jps->printf->jstack等一系列的操作。本篇介紹一個jvm工具,它是阿里巴巴開源的一個工具——arthas(阿爾薩斯)。
一、安裝
arthas在github上有個page,地址是https://alibaba.github.io/arthas/。
安裝的方式有好幾種:
- 直接下載一個可以啟動的jar包然后用java -jar的方式啟動
- 用官方提供的as.sh腳本一鍵安裝
- 用rpm的方式安裝
本篇介紹第一種方式,因為它簡單而且想遷移的時候也超級方便(畢竟只需要把下載的jar包拷貝走就行了)。
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
如果下載速度太慢,可以用gitee上的源
curl -O https://arthas.gitee.io/arthas-boot.jar
curl命令直接把arthas-boot.jar下載到你想要的目錄
[root@localhost ~]# ll -lrt
-rw-r--r--. 1 root root 138880 Jun 22 02:55 arthas-boot.jar
二、啟動
用java命令直接啟動
[root@localhost ~]# java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.3.3
[INFO] Can not find java process. Try to pass <pid> in command line.
Please select an available pid.
但是這里啟動失敗了,這是因為arthas在啟動時會檢測本機運行的jvm進程,然后讓用戶選擇需要綁定的進程,后面的操作都是針對選定的進程的。
這里我先啟動一個java應用,然后再啟動arthas。
[root@localhost ~]# java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.3.3
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 2467 jvm-0.0.1-SNAPSHOT.jar
下面就列出了本機正在運行的java進程,等待用戶輸入,這里輸入1然后回車。如果是第一次啟動需要下載一些必要的文件,等待下載完成即可。
[root@localhost arthas]# java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.3.3
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 2467 jvm-0.0.1-SNAPSHOT.jar
1
[INFO] arthas home: /usr/local/arthas
[INFO] Try to attach process 2467
[INFO] Attach process 2467 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://alibaba.github.io/arthas
tutorials https://alibaba.github.io/arthas/arthas-tutorials
version 3.3.3
pid 2467
time 2020-06-22 03:02:31
[arthas@2467]$
如果看到這個界面就表示啟動並關聯成功了。
三、help命令
在arthas交互環境中,可以輸入help命令,然后會出現所有arthas支持的命令
[arthas@2467]$ help
NAME DESCRIPTION
help Display Arthas Help
keymap Display all the available keymap for the specified connection.
sc Search all the classes loaded by JVM
sm Search the method of classes loaded by JVM
classloader Show classloader info
jad Decompile class
getstatic Show the static field of a class
...
如果不知道命令的用法,可以輸入相應的命令后加參數--help,比如可以看一下sc命令的用法
[arthas@2467]$ sc --help
USAGE:
sc [-c <value>] [-d] [-x <value>] [-f] [-h] [-E] class-pattern
SUMMARY:
Search all the classes loaded by JVM
EXAMPLES:
sc -d org.apache.commons.lang.StringUtils
sc -d org/apache/commons/lang/StringUtils
sc -d *StringUtils
sc -d -f org.apache.commons.lang.StringUtils
sc -E org\\.apache\\.commons\\.lang\\.StringUtils
WIKI:
https://alibaba.github.io/arthas/sc
OPTIONS:
-c, --classloader <value> The hash code of the special class's classLoader
-d, --details Display the details of class
-x, --expand <value> Expand level of object (0 by default)
-f, --field Display all the member variables
-h, --help this help
-E, --regex Enable regular expression to match (wildcard matching by default)
<class-pattern> Class name pattern, use either '.' or '/' as separator
不僅會顯示出命令是干嘛用的,命令的完整參數,還很貼心地提供了一些具體的例子,如果英語看不習慣,還可以到WIKI下面那個地址看官方文檔,有中文版的。
四、用arthas解決上一篇的問題
(1)cpu占用過高
用thread命令列出線程的信息
[arthas@2467]$ thread
Threads Total: 28, NEW: 0, RUNNABLE: 11, BLOCKED: 0, WAITING: 14, TIMED_WAITING: 3, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTE DAEMON
16 http-nio-8080-exec-2 main 5 RUNNABLE 99 0:25 false true
29 Attach Listener system 9 RUNNABLE 0 0:0 false true
11 Catalina-utility-1 main 1 WAITING 0 0:0 false false
12 Catalina-utility-2 main 1 TIMED_WAIT 0 0:0 false false
28 DestroyJavaVM main 5 RUNNABLE 0 0:4 false false
3 Finalizer system 8 WAITING 0 0:0 false true
2 Reference Handler system 10 WAITING 0 0:0 false true
這個命令會把所有線程按照cpu占用率從高到低列出來,如果線程太多,可以通過-n參數指定輸出的行數。
上面的輸出結果可以看到id為16的這個線程cpu占用率很過,然后再通過thread加線程id輸出改線程的棧信息
[arthas@2467]$ thread 16
"http-nio-8080-exec-2" Id=16 RUNNABLE
at com.spareyaya.jvm.service.EndlessLoopService.service(EndlessLoopService.java:19)
at com.spareyaya.jvm.controller.JVMController.endlessLoop(JVMController.java:30)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
...
兩步就定位到了問題
(2)死鎖
還是用thread命令,參數是-b
[arthas@2997]$ thread -b
"Thread-3" Id=29 BLOCKED on java.lang.Object@3f20bf9 owned by "Thread-4" Id=30
at com.spareyaya.jvm.service.DeadLockService.service1(DeadLockService.java:27)
- blocked on java.lang.Object@3f20bf9
- locked java.lang.Object@2fea801a <---- but blocks 1 other threads!
at com.spareyaya.jvm.controller.JVMController.lambda$deadLock$0(JVMController.java:37)
at com.spareyaya.jvm.controller.JVMController$$Lambda$456/748979989.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
這個命令和jstack工具檢測死鎖同樣簡單,不過個人認為jstack工具檢測死鎖其實要比這個更直觀一些。
(3)內存泄漏
這個我們可以用dashboard命令來動態查看內存情況
如果內容使用率在不斷上升,而且gc后也不下降,后面還發現gc越來越頻繁,很可能就是內存泄漏了。
這個時候我們可以直接用heapdump命令把內存快照dump出來,作用和jmap工具一樣
[arthas@23581]$ heapdump --live /root/jvm.hprof
Dumping heap to /root/jvm.hprof...
Heap dump file created
然后把得到的dump文件導入eclipse,用MAT插件分析就行了。
五、arthas其它命令
arthas還提供了很多用於監控的命令,比如監控某個方法的執行時間,反編譯線上的class文件,甚至在不重啟java應用的情況下直接替換某個類。官方的使用文檔已經寫得太詳細了,這里就不再一一介紹了,大家可以自己嘗試。
六、再說MAT工具
上一篇和本篇在排查內存泄漏的時候我們都用到了同一個工具來分析——MAT。之前我們是在eclipse中安裝了MAT插件,使用的時候只能打開eclipse來用。問題是,現在使用eclipse作為開發工具的移動互聯網公司應該很少了,我們也不想每次分析內存快照時都要啟動一個eclipse。
所以這里介紹一個MAT的獨立工具,它是獨立於eclipse的應用,下載地址是https://www.eclipse.org/mat/downloads.php,可以根據自己的系統選擇版本。
比如在windows下可以直接雙擊MemoryAnalyzer.exe啟動,啟動后可以通過頂部菜單的File->Open Heap Dump...來打開一個快照文件,也可以在welcome界面中點擊Open a Heap Dump。如果你的快照文件特別大,需要調整jvm參數,在windows下修改MemoryAnalyzer.ini文件,把-Xmx參數的值設置成適合的值(默認是1024M)。
在Overview選擇卡中,可以選擇需要分析的內容。比如可以點擊Leak Suspects分析可能的內存泄漏,也可以點擊Histogram來查看每個類的實例統計。
然后重點關注那些實例數目特別多的,或者占用內存特別多的(這個還可以設置正則表達式進行過濾,在大項目時很有用),然后結合自己的代碼看看這些對象是不是真正都需要的,還是因為作用域設置得太大了導致沒有及時回收造成。
總之,分析內存快照其實是一項費時費力的工作,在分析中積累經驗其它很重要,工具只是為了提高分析的效率。
至於像JProfile這種商業版專業的jvm分析工具,也可以去多了解。