Arthas 是阿裡開源的 Java 診斷工具。線上排查問題,無需重啟;動態跟蹤 Java 代碼;實時監控 JVM 狀態。Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,採用命令行交互模式,同時提供豐富的 Tab 自動補全功能,進一步方便進行問題的定位和診斷。 Artha ...
Arthas 是阿裡開源的 Java 診斷工具。線上排查問題,無需重啟;動態跟蹤 Java 代碼;實時監控 JVM 狀態。Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,採用命令行交互模式,同時提供豐富的 Tab 自動補全功能,進一步方便進行問題的定位和診斷。
Arthas可以通過簡單的命令交互模式,接入運行的JVM,快速定位和診斷線上程式運行的問題。在不重啟服務的情況下,實時,動態的修改相關代碼,並實時生效,具體工作原理如下:
- 連接JVM,通過attach機制,通過attach pid連接正在運行的JVM
- 查看及修改JVM位元組碼,通過instrument技術對運行中的JVM附加或修改位元組碼來實現增強的邏輯
Arthas的執行過程如下:Arthas底層調用rt.jar包的ManagementFactory獲取整個JVM內部信息,通過命令集成與後端交互,執行,返回結果,整個工程簡單清晰,容易上手。
arthas-demo入門
可以使用阿裡雲給的基礎教程地址練習: https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=arthas-basics
在這裡,我使用自己的伺服器,跟著基礎教程做入門聯繫。
1.下載math-game.jar
,再用java -jar
命令啟動
[root@localhost arthas]# wget https://arthas.aliyun.com/math-game.jar
[root@localhost arthas]# java -jar arthas-boot.jar
2.新開Terminal
,下載arthas-boot.jar
,再用java -jar
命令啟動
[root@localhost arthas]# wget https://arthas.aliyun.com/arthas-boot.jar
[root@localhost arthas]# java -jar arthas-boot.jar
arthas-boot
是Arthas
的啟動程式,它啟動後,會列出所有的Java進程,用戶可以選擇需要診斷的目標進程。
選擇第一個進程,輸入 1
(math-game這個程式對應的就是1),再Enter/回車
:
Attach成功之後,會列印Arthas LOGO。輸入 help
可以獲取到更多的幫助信息。
3. dashboard
命令可以查看當前系統的實時數據面板
數據說明:
- ID:java級別的線程ID,註意這個ID不能跟jstack中的nativeID一一對應
- NAME:線程名
- GROUP:線程組名
- PRIORI:線程的優先順序,1~10之間的數字,越大表示優先順序越高
- STATE:線程的狀態
- %CPU:線程的CPU使用率,比如採樣間隔1000ms,某個線程的增量cpu時間為100ms,則cpu的使用率為100/1000=10%
- DELTA_TIME:上次採樣之後線程運行增量cpu時間,數據格式為秒
- TIME:線程運行總CPU時間,數據格式為 分:秒
- INTERRUPTED:線程當前的中斷位狀態
- DAEMON:是否是daemon(後臺)線程
4. thread
命令會列印線程ID 1的棧
還可以通過thread 1 | grep 'main('
命令來查找main class:
參數說明:
參數名稱 | 參數說明 |
---|---|
id | 線程id |
[ n: ] | 指定最忙的前N個線程並列印堆棧 |
[ b ] | 找出當前阻塞其他線程的線程(目前只支持找出synchronized關鍵字阻塞住的線程, 如果是java.util.concurrent.Lock , 目前還不支持。) |
[ i ] | 指定cpu占比統計的採樣間隔,單位為毫秒 |
[ --all ] | 顯示所有匹配的線程 |
[ --state ] | 查看指定狀態的線程,如: thread --state WAITING |
5. sc
命令來查找JVM里已載入的類
sc為“Search-Class” 的簡寫,能搜索出所有已經載入到 JVM 中的 Class 信息。
參數說明:
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配,支持全限定名。如:demo.MathGame,也支持demo/MathGame |
method-pattern | 方法名錶達式匹配 |
[ d ] | 輸出當前類的詳細信息,包括這個類所載入的原始文件來源,類的聲明,載入的ClassLoader等詳細信息;如果一個類被多個ClassLoader所載入,則會出現多次 |
[ E ] | 開啟正則表達式匹配,預設為通配符匹配 |
[ f ] | 輸出當前類的成員變數信息(需要配合參數 -d一起使用) |
[ x: ] | 指定輸出靜態變數時屬性的遍歷深度,預設為0,直接使用toString輸出 |
[ c: ] | 指定class的ClassLoader的hashcode |
[ classLoaderClass: ] | 指定執行表達式的ClassLoader的class name |
[ n: ] | 具有詳細信息的匹配類的最大數量,預設為100 |
sm為“Search-Method” 的簡寫,查詢某個類下所有的方法,與sc的功能類似,這裡就不詳細介紹了。
6. jad
命令來反編譯代碼
還可以反編譯指定的函數
反編譯時只顯示源代碼
預設情況下,反編譯結果里會帶有ClassLoader
信息,通過--source-only
選項,可以只列印源代碼。這樣就會清爽很多。
7. watch
方法執行數據觀測
watch命令可以查看函數的參數/返回值/異常信息,通過編寫 OGNL 表達式進行對應變數的查看。
從上面的結果里,說明函數被執行了兩次,第一次結果是 location=AtExceptionExit,說明函數拋出了異常,因此returnObj是null;第二次結果是location=AtExit,說明函數正常返回,因此可以看到returnObj的結果是一個ArrayList。
參數說明:
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
express | 觀察表達式,預設值為:{params,target,returnObj},單個值可以不用加 {},多個值需要加 |
condition-express | 條件表達式,不能加 {},可以使用逗號分隔子表達式,取表達式最後一個值來判斷 |
[ b ] | 在方法調用之前觀察,預設關閉,由於觀察事件點是在方法調用前,此時返回值或異常均不存在,params代表方法入參 |
[ e ] | 在方法異常之後觀察,預設關閉,params代表方法出參 |
[ s ] | 在方法返回之後觀察,預設關閉,params代表方法出參 |
[ f ] | 在方法結束之後(正常返回和異常返回)觀察,預設打開,params代表方法出參 |
[ E ] | 開啟正則表達式匹配,預設為通配符匹配 |
[ x: ] | 指定輸出結果的屬性遍歷深度,預設為1 |
[ #cost ] | 監控耗時 |
條件表達式的例子:
下麵這個例子表示只有參數小於0的調用才會響應。
異常信息的例子:
-e 表示拋出異常時才觸發
express中,表示異常信息的變數時throwExp
8. vmtool
命令,可以搜索記憶體對象
vmtool
利用JVMTI
介面,實現查詢記憶體對象,強制GC等功能。
參數說明:
參數名稱 | 參數說明 |
---|---|
--action getInstances | 返回結果綁定到 instances變數上,它是數組。 |
--className | 指定類名(完成路徑),支持 java.lang.String,也支持java/lang/String |
[ --limit ] | 限制返回值數量,避免獲取超大數據時對JVM造成壓力。預設值是10。 |
[ -x ] | 指定返回結果展開層數,預設為1 |
[ c: ] | 指定class的ClassLoader的hashcode(通過sc命令找到載入class的classLoader) |
[ classLoaderClass: ] | 指定執行表達式的ClassLoader的class name |
強制GC的命令:vmtool --action forceGc
9. 退出Arthas
用 exit
或者 quit
命令可以退出Arthas。退出Arthas之後,還可以再次用 java -jar arthas-boot.jar
來連接。
10. 徹底退出Arthas
exit/quit
命令只是退出當前session,arthas server還在目標進程中運行。
想完全退出Arthas,可以執行 stop
命令。
Arthas的其他重點使用功能
1. mc 記憶體編譯器(Memory Compiler/記憶體編譯器)
Memory Compiler/記憶體編譯器,編譯.java
文件生成.class
。通過 -c / --classLoaderClass 參數指定classLoader,-d 參數指定輸出目錄;編譯生成.class文件之後,可以結合retransform
命令實現熱更新代碼。retransform的限制:1.不允許新增加field和method 2.正在跑的函數,沒有退出不能生效
這裡還是使用arthas的提供的教程:https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-mc-retransform
1.由於正在跑的函數沒有退出時不生效的,所以上面的math-game的demo就不能使用了,所以得下載另一個demo(一個簡單的spring-boot應用)。
[root@localhost arthas]# wget https://raw.githubusercontent.com/hengyunabc/spring-boot-inside/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar
[root@localhost arthas]# java -jar demo-arthas-spring-boot.jar
2.新開一個Terminal
;訪問下麵這個路徑,可以看到報錯(500異常)了。
3.啟動arthas-boot應用。
4.反編譯代碼可以看到,當id小於1是,就會拋出異常。
在這裡,我們修改文件,想讓id小於1時還是能正常返回,不拋出異常。
5.jad反編譯UserController,將結果保存在/tmp/UserController.java
文件夾里。
[arthas@1645]$ jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
6.通過vim /tmp/UserController.java
編輯java類。
7.通過sc查找載入UserContoller的ClassLoader,也可以在jad時顯示源碼,裡面也有classLoader的信息。
以下三個命令任意一個都可以。可以看到,這個java類是由LaunchedURLClassLoader@1be6f5c3
這個類載入器載入的。
8.通過mc命令編譯,同時指定--classLoaderClass
參數指定ClassLoader:
[arthas@1645]$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
也可以通過-c
參數指定ClassLoaderHash:
[arthas@1645]$ mc --c 1be6f5c3 /tmp/UserController.java -d /tmp
可以看到,在tmp文件夾下,根據UserController的類的全路徑編譯了一個class文件:
9.retransform 命令中西載入新編譯好的UserContoller.class類:
10.重新編譯文件可以看到,代碼已經替換成最新的了:
11.訪問:
顯示已經替換過的類:retransform -l
恢復修改前的代碼,清楚指定的類:retransform -d 1
清楚所有的:retransform --deleteAll
2.trace 方法內部調用路徑,並輸出方法路徑上的每個節點上耗時
可以觀察方法執行的時候哪個子調用比較慢
[arthas@1645]$ trace ClassName methodName
trace
命令能主動搜索 class-pattern
/method-pattern
對應的方法調用路徑,渲染和統計整個調用鏈路上的所有性能開銷和追蹤調用鏈路。在進行性能調優的時候十分有效。
參數說明:
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
express | 觀察表達式,預設值為:{params,target,returnObj},單個值可以不用加 {},多個值需要加 |
condition-express | 條件表達式 |
[ E ] | 開啟正則表達式匹配,預設為通配符匹配 |
[ n: ] | 命令執行次數 |
[ #cost ] | 方法執行耗時 |
[ --skipJDKMethod ] | 跳過jdk方法,預設為true |
不跳過JDK方法:
只展示耗時大於1ms的調用路徑:
動態trace:
從上圖中可以看到,primeFactors的方法耗時最長,如果想深入primeFactors方法,可以打開一個新的終端,使用telnet localhost 3658
連接上arthas,在trace primeFactors時指定listenerId。
這時終端2列印的結果,說明已經增強了一個函數:Affect(class count: 1 , method count: 1)
,但不再列印更多的結果。
再查看終端1,可以發現trace的結果增加了一層,列印了primeFactors
函數里的內容:
註意 --listenerId指定的id在前一條命令的輸出中可以看到。
3.stack 查看某個函數的調用堆棧路徑
很多時候,在一個方法被執行時,方法的執行路徑非常多,或者根本就不著調這個方法時從哪裡被執行的,就可以使用stack命令。此命令和trace命令結構類似。
[arthas@1645]$ stack demo.MathGame primeFactors
參數說明:
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
express | 觀察表達式,預設值為:{params,target,returnObj},單個值可以不用加 {},多個值需要加 |
condition-express | 條件表達式 |
[ E ] | 開啟正則表達式匹配,預設為通配符匹配 |
[ n: ] | 命令執行次數 |
4.tt 命令
tt是TimeTunnel 的縮寫,tt命令記錄方法執行數據的時空隧道,記錄下指定方法每次調用的入參和返回信息,並能對這些不同的時間下調用進行觀測。
watch
雖然很方便和靈活,但需要提前想清楚觀察表達式的拼寫,這對排查問題而言要求太高,因為很多時候我們並不清楚問題出自於何方,只能靠蛛絲馬跡進行猜測。
這個時候如果能記錄下當時方法調用的所有入參和返回值、拋出的異常會對整個問題的思考與判斷非常有幫助。
參數說明:
參數名稱 | 參數說明 |
---|---|
-t | 記錄下類對應的方法的每次執行情況 |
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
[ n: ] | 命令執行次數 |
condition-express | 條件表達式 |
表格欄位說明:
表格欄位 | 欄位解釋 |
---|---|
index | 時間片段記錄編號,每一個編號代表著一次調用,後續tt還有很多命令都是基於此編號指定記錄操作,非常重要。 |
timestamp | 方法執行的本機時間,記錄了這個時間片段所發生的本機時間 |
cost(ms) | 方法執行的耗時 |
is-ret | 方法是否以正常返回的形式結束 |
is-exp | 方法是否以拋異常的形式結束 |
object | 執行對象的hashCode() ,註意,曾經有人誤認為是對象在JVM中的記憶體地址,但很遺憾他不是。但他能幫助你簡單的標記當前執行方法的類實體 |
class | 執行的類名 |
method | 執行的方法名 |
檢索調用記錄:
tt -l
檢索所有的調用記錄:
篩選出 primeFactors
方法的調用信息:
通過 -i
參數後邊跟著對應的 INDEX
編號查看到他的詳細信息:
重做一次調用:
tt
命令由於保存了當時調用的所有現場信息,所以我們可以自己主動對一個 INDEX
編號的時間片自主發起一次調用。此時需要使用 -p
參數。通過 --replay-times
指定 調用次數,通過 --replay-interval
指定多次調用間隔(單位ms, 預設1000ms)
需要強調的點
-
ThreadLocal 信息丟失
很多框架偷偷的將一些環境變數信息塞到了發起調用線程的 ThreadLocal 中,由於調用線程發生了變化,這些 ThreadLocal 線程信息無法通過 Arthas 保存,所以這些信息將會丟失。
-
引用的對象
需要強調的是,
tt
命令是將當前環境的對象引用保存起來,但僅僅也只能保存一個引用而已。如果方法內部對入參進行了變更,或者返回的對象經過了後續的處理,那麼在tt
查看的時候將無法看到當時最準確的值。這也是為什麼watch
命令存在的意義。
5.monitor 方法執行監控
對匹配的class-pattern
/ method-pattern
的類、方法的調用進行監控。monitor
命令是一個非實時返回的命令(並不是輸入之後立即返回,而是不斷的等待目標java進程返回信息)。
參數說明:
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
[ E ] | 開啟正則表達式匹配,預設為通配符匹配 |
[ c: ] | 統計周期,預設值為120秒,是一個整型的參數值 |
監控項說明:
監控項 | timestamp | class | method | total | success | tail | rt | fail-rate |
---|---|---|---|---|---|---|---|---|
說明 | 時間戳 | java類 | 方法 | 調用次數 | 成功次數 | 失敗次數 | 平均rt | 失敗率 |
5.target-ip
target-ip 為指定綁定的IP,如果不指定IP,Arthas只listen 127.0.0.1,所以如果想從遠程連接,則可以使用 --target-ip參數指定listen的IP。
java -jar arthas-boot.jar --target-ip IP
綁定遠程訪問IP後,可以在通過telnet 或者http的方式遠程連接Arthas進行問題排查
還有更多的功能請查看arthas的官方文檔。
本文版權歸Charon和博客園共有,原創文章,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。