Arthas進階-筆記

☞茶凉發表於2024-07-06

《Arthas進階》

學習目標

  1. 類和類載入器相關的命令
  2. monitor/watch/trace/stack等核心命令的使用
  3. 火焰圖的生成
  4. Arthas實戰案例

dump

作用

將已載入類的位元組碼檔案儲存到特定目錄:logs/arthas/classdump/

引數

數名稱 引數說明
class-pattern 類名錶達式匹配
[c:] 類所屬 ClassLoader 的 hashcode
[E] 開啟正規表示式匹配,預設為萬用字元匹配

舉例

把String類的位元組碼檔案儲存到~/logs/arthas/classdump/目錄下
dump java.lang.String
把demo包下所有的類的位元組碼檔案儲存到~/logs/arthas/classdump/目錄下
dump demo.*

效果

image-20200310223714230

image-20200310223850169 image-20200310224236313

小結

dump作用:將正在JVM中執行的程式的位元組碼檔案提取出來,儲存在logs相應的目錄下

不同的類載入器放在不同的目錄下

classloader

目標

獲取類載入器的資訊

作用

  1. classloader 命令將 JVM 中所有的classloader的資訊統計出來,並可以展示繼承樹,urls等。

  2. 可以讓指定的classloader去getResources,列印出所有查詢到的resources的url。對於ResourceNotFoundException異常比較有用。

引數說明

引數名稱 引數說明
[l] 按類載入例項進行統計
[t] 列印所有ClassLoader的繼承樹
[a] 列出所有ClassLoader載入的類,請謹慎使用
[c:] ClassLoader的hashcode
[c: r:] 用ClassLoader去查詢resource
[c: load:] 用ClassLoader去載入指定的類

舉例

預設按類載入器的型別檢視統計資訊
classloader
image-20200311092606080
按類載入器的例項檢視統計資訊,可以看到類載入的hashCode
classloader -l

image-20200311092709402

檢視ClassLoader的繼承樹
classloader -t
image-20200311092759627
透過類載入器的hash,檢視此類載入器實際所在的位置
classloader -c 680f2737
image-20200311093005661
使用ClassLoader去查詢指定資源resource所在的位置
classloader -c 680f2737 -r META-INF/MANIFEST.MF
image-20200311093248859
使用ClassLoader去查詢類的class檔案所在的位置
classloader -c 680f2737 -r java/lang/String.class
image-20200311094534683
使用ClassLoader去載入類
classloader -c 70dea4e --load java.lang.String
image-20200311094910635

小結

classloader命令主要作用有哪些?

  1. 顯示所有類載入器的資訊
  2. 獲取某個類載入器所在的jar包
  3. 獲取某個資源在哪個jar包中
  4. 載入某個類

monitor

目標

monitor命令:監控指定類中方法的執行情況

作用

對匹配 class-patternmethod-pattern的類、方法的呼叫進行監控。

monitor 命令是一個非實時返回命令,實時返回命令是輸入之後立即返回

而非實時返回的命令,則是不斷的等待目標 Java 程序返回資訊,直到使用者輸入 Ctrl+C 為止。

引數說明

方法擁有一個命名引數 [c:],意思是統計週期(cycle of output),擁有一個整型的引數值

引數名稱 引數說明
class-pattern 類名錶達式匹配
method-pattern 方法名錶達式匹配
[E] 開啟正規表示式匹配,預設為萬用字元匹配
[c:] 統計週期,預設值為120秒

舉例

過5秒監控一次,類demo.MathGame中primeFactors方法
monitor -c 5 demo.MathGame primeFactors

image-20200311101017085

監控的維度說明

監控項 說明
timestamp 時間戳
class Java類
method 方法(構造方法、普通方法)
total 呼叫次數
success 成功次數
fail 失敗次數
rt 平均耗時
fail-rate 失敗率

小結

monitor命令的作用是什麼?

用來監視一個時間段中指定方法的執行次數,成功次數,失敗次數,耗時等這些資訊

watch

目標

觀察到指定方法的呼叫情況

作用

方法執行資料觀測,讓你能方便的觀察到指定方法的呼叫情況。

能觀察到的範圍為:返回值丟擲異常入參,透過編寫OGNL 表示式進行對應變數的檢視。

引數說明

watch 的引數比較多,主要是因為它能在 4 個不同的場景觀察物件

引數名稱 引數說明
class-pattern 類名錶達式匹配
method-pattern 方法名錶達式匹配
express 觀察表示式
condition-express 條件表示式
[b] 方法呼叫之前觀察 before
[e] 方法異常之後觀察 exception
[s] 方法返回之後觀察 success
[f] 方法結束之後(正常返回和異常返回)觀察 finish
[E] 開啟正規表示式匹配,預設為萬用字元匹配
[x:] 指定輸出結果的屬性遍歷深度,預設為 1

這裡重點要說明的是觀察表示式,觀察表示式的構成主要由ognl 表示式組成,所以你可以這樣寫"{params,returnObj}",只要是一個合法的 ognl 表示式,都能被正常支援。

特別說明

  • watch 命令定義了4個觀察事件點,即 -b 方法呼叫前,-e 方法異常後,-s 方法返回後,-f 方法結束後
  • 4個觀察事件點 -b-e-s 預設關閉,-f 預設開啟,當指定觀察點被開啟後,在相應事件點會對觀察表示式進行求值並輸出
  • 這裡要注意方法入參方法出參的區別,有可能在中間被修改導致前後不一致,除了 -b 事件點 params 代表方法入參外,其餘事件都代表方法出參
  • 當使用 -b 時,由於觀察事件點是在方法呼叫前,此時返回值或異常均不存在

舉例

觀察demo.MathGame類中primeFactors方法出參和返回值,結果屬性遍歷深度為2。
params表示所有引數陣列(因為不確定是幾個引數),returnObject表示返回值

watch demo.MathGame primeFactors "{params,returnObj}" -x 2
image-20200311103008904
觀察方法入參,對比前一個例子,返回值為空(事件點為方法執行前,因此獲取不到返回值)

watch demo.MathGame primeFactors "{params,returnObj}" -x 2 -b
image-20200311152248842
觀察當前物件中的屬性,如果想檢視方法執行前後,當前物件中的屬性,可以使用target關鍵字,代表當前物件
watch demo.MathGame primeFactors 'target' -x 2
image-20200311153804431
同時觀察方法呼叫前和方法返回後,引數裡-n 2,表示只執行兩次。
這裡輸出結果中,第一次輸出的是方法呼叫前的觀察表示式的結果,第二次輸出的是方法返回後的表示式的結果
params表示引數,target表示執行方法的物件,returnObject表示返回值

watch demo.MathGame primeFactors "{params,target,returnObj}" -x 2 -b -s -n 2
image-20200311153004991
使用target.field_name訪問當前物件的某個屬性
watch demo.MathGame primeFactors 'target.illegalArgumentCount'
image-20200311154005898
條件表示式的例子,輸出第1引數小於的情況
watch demo.MathGame primeFactors "{params[0],target}" "params[0]<0"
image-20200311155053465

小結

引數名稱 引數說明
[b] begin 監視方法執行前的情況
[e] exception 監視出現異常的情況
[s] success 執行成功的情況
[f] finish 執行完畢的情況,包含成功或失敗

trace

目標

學習trace這條命令的使用

對方法內部呼叫路徑進行追蹤,並輸出方法路徑上的每個節點上耗時

介紹

trace 命令能主動搜尋 class-patternmethod-pattern 對應的方法呼叫路徑,渲染和統計整個呼叫鏈路上的所有效能開銷和追蹤呼叫鏈路。

觀察表示式的構成主要由ognl 表示式組成,所以你可以這樣寫"{params,returnObj}",只要是一個合法的 ognl 表示式,都能被正常支援。

很多時候我們只想看到某個方法的rt大於某個時間之後的trace結果,現在Arthas可以按照方法執行的耗時來進行過濾了,例如trace *StringUtils isBlank '#cost>100'表示當執行時間超過100ms的時候,才會輸出trace的結果。

watch/stack/trace這個三個命令都支援#cost耗時條件過濾

引數說明

引數名稱 引數說明
class-pattern 類名錶達匹配
method-pattern 方法名錶達式匹配
condition-express 條件表示式,使用OGNL表示式
[E] 開啟正規表示式匹配,預設是萬用字元匹配
[n:] 設定命令執行次數
#cost 方法執行耗時,單位是毫秒

舉例

trace函式指定類的指定方法
trace demo.MathGame run

image-20200311171209739

如果方法呼叫的次數很多,那麼可以用-n引數指定捕捉結果的次數。比如下面的例子裡,捕捉到一次呼叫就退出命令。
trace demo.MathGame run -n 1

image-20200311171239100

預設情況下,trace不會包含jdk裡的函式呼叫,如果希望trace jdk裡的函式,需要顯式設定--skipJDKMethod false。
trace --skipJDKMethod false demo.MathGame run

image-20200311171325682

據呼叫耗時過濾,trace大於0.5ms的呼叫路徑
trace demo.MathGame run '#cost > .5'
image-20200311171441982
可以用正則表匹配路徑上的多個類和函式,一定程度上達到多層trace的效果。
trace -E com.test.ClassA|org.test.ClassB method1|method2|method3

小結

引數名稱 引數說明
class-pattern 類名錶達式匹配
method-pattern 方法名錶達式匹配
condition-express 條件表示式
#cost 過濾條件,只追蹤滿足的耗時方法

stack

作用

輸出當前方法被呼叫的呼叫路徑

介紹

很多時候我們都知道一個方法被執行,但這個方法被執行的路徑非常多,或者你根本就不知道這個方法是從那裡被執行了,此時你需要的是 stack 命令。

引數說明

引數名稱 引數說明
class-pattern 類名錶達式匹配
method-pattern 方法名錶達式匹配
condition-express 條件表示式,OGNL
[E] 開啟正規表示式匹配,預設為萬用字元匹配
[n:] 執行次數限制

舉例

獲取primeFactors的呼叫路徑
stack demo.MathGame primeFactors
image-20200311173954382
條件表示式來過濾,第0個引數的值小於0,-n表示獲取2次
stack demo.MathGame primeFactors 'params[0]<0' -n 2
image-20200311174229013
據執行時間來過濾,耗時大於0.5毫秒
stack demo.MathGame primeFactors '#cost>0.5'
image-20200311174408523

小結

stack命令的作用是什麼?

輸出當前方法被呼叫的路徑

tt

作用

time-tunnel 時間隧道

記錄下指定方法每次呼叫的入參和返回資訊,並能對這些不同時間下呼叫的資訊進行觀測

介紹

watch 雖然很方便和靈活,但需要提前想清楚觀察表示式的拼寫,這對排查問題而言要求太高,因為很多時候我們並不清楚問題出自於何方,只能靠蛛絲馬跡進行猜測。

這個時候如果能記錄下當時方法呼叫的所有入參和返回值、丟擲的異常會對整個問題的思考與判斷非常有幫助。

於是乎,TimeTunnel 命令就誕生了。

引數解析

tt的引數 說明
-t 記錄某個方法在一個時間段中的呼叫
-l 顯示所有已經記錄的列表
-n 次數 只記錄多少次
-s 表示式 搜尋表示式
-i 索引號 檢視指定索引號的詳細呼叫資訊
-p 重新呼叫指定的索引號時間碎片
  • -t

    tt 命令有很多個主引數,-t 就是其中之一。這個參數列明希望記錄下類 *Testprint 方法的每次執行情況。

  • -n 3

    當你執行一個呼叫量不高的方法時可能你還能有足夠的時間用 CTRL+C 中斷 tt 命令記錄的過程,但如果遇到呼叫量非常大的方法,瞬間就能將你的 JVM 記憶體撐爆。

    此時你可以透過 -n 引數指定你需要記錄的次數,當達到記錄次數時 Arthas 會主動中斷tt命令的記錄過程,避免人工操作無法停止的情況。

使用案例

基本使用

最基本的使用來說,就是記錄下當前方法的每次呼叫環境現場。
tt -t demo.MathGame primeFactors

image-20200321200911157

表格欄位說明

表格欄位 欄位解釋
INDEX 時間片段記錄編號,每一個編號代表著一次呼叫,後續tt還有很多命令都是基於此編號指定記錄操作,非常重要。
TIMESTAMP 方法執行的本機時間,記錄了這個時間片段所發生的本機時間
COST(ms) 方法執行的耗時
IS-RET 方法是否以正常返回的形式結束
IS-EXP 方法是否以拋異常的形式結束
OBJECT 執行物件的hashCode(),注意,曾經有人誤認為是物件在JVM中的記憶體地址,但很遺憾他不是。但他能幫助你簡單的標記當前執行方法的類實體
CLASS 執行的類名
METHOD 執行的方法名
  • 條件表示式

    不知道大家是否有在使用過程中遇到以下困惑

    • Arthas 似乎很難區分出過載的方法
    • 我只需要觀察特定引數,但是 tt 卻全部都給我記錄了下來

    條件表示式也是用 OGNL 來編寫,核心的判斷物件依然是 Advice 物件。除了 tt 命令之外,watchtracestack 命令也都支援條件表示式。

  • 解決方法過載

    tt -t *Test print params.length==1

    透過制定引數個數的形式解決不同的方法簽名,如果引數個數一樣,你還可以這樣寫

    tt -t *Test print 'params[1] instanceof Integer'

  • 解決指定引數

    tt -t *Test print params[0].mobile=="13989838402"

檢索呼叫記錄

當你用 tt 記錄了一大片的時間片段之後,你希望能從中篩選出自己需要的時間片段,這個時候你就需要對現有記錄進行檢索。

tt -l

image-20200321194350208

需要篩選出 primeFactors 方法的呼叫資訊

tt -s 'method.name=="primeFactors"'

image-20200321194724694

檢視呼叫資訊

對於具體一個時間片的資訊而言,你可以透過 -i 引數後邊跟著對應的 INDEX 編號檢視到他的詳細資訊。

tt -i 1002
image-20200321201648590

重做一次呼叫

當你稍稍做了一些調整之後,你可能需要前端系統重新觸發一次你的呼叫,此時得求爺爺告奶奶的需要前端配合聯調的同學再次發起一次呼叫。而有些場景下,這個呼叫不是這麼好觸發的。

tt 命令由於儲存了當時呼叫的所有現場資訊,所以我們可以自己主動對一個 INDEX 編號的時間片自主發起一次呼叫,從而解放你的溝通成本。此時你需要 -p 引數。透過 --replay-times 指定 呼叫次數,透過 --replay-interval 指定多次呼叫間隔(單位ms, 預設1000ms)

tt -i 1002 -p
image-20200321195211743

小結

作用:記錄指定方法每次呼叫的入參和返回值,並後期還可以對這些資訊進行觀測

tt的引數 說明
-t 記錄方法在一個時間段中呼叫
-l 顯示所有已經記錄的列表
-n 次數 只記錄多少次
-s 搜尋表示式 指定搜尋表示式
-i 索引號 檢視它的詳細資訊

options

作用

全域性開關

全域性選項

名稱 預設值 描述
unsafe false 是否支援對系統級別的類進行增強,開啟該開關可能導致把JVM搞掛,請慎重選擇!
dump false 是否支援被增強了的類dump到外部檔案中,如果開啟開關,class檔案會被dump到/${application dir}/arthas-class-dump/目錄下,具體位置詳見控制檯輸出
batch-re-transform true 是否支援批次對匹配到的類執行retransform操作
json-format false 是否支援json化的輸出
disable-sub-class false 是否禁用子類匹配,預設在匹配目標類的時候會預設匹配到其子類,如果想精確匹配,可以關閉此開關
debug-for-asm false 列印ASM相關的除錯資訊
save-result false 是否開啟執行結果存日誌功能,開啟之後所有命令的執行結果都將儲存到~/logs/arthas-cache/result.log
job-timeout 1d 非同步後臺任務的預設超時時間,超過這個時間,任務自動停止;比如設定 1d, 2h, 3m, 25s,分別代表天、小時、分、秒
print-parent-fields true 是否列印在parent class裡的filed

案例

檢視所有的options

options

image-20200322092041440

獲取option的值

options json-format

image-20200322092136758

設定指定的option

例如,想開啟執行結果存日誌功能,輸入如下命令即可:

options save-result true

image-20200322092300964

小結

options的作用是:檢視或設定arthas全域性環境變數

profiler火焰圖

目標

生成火焰圖

介紹

profiler 命令支援生成應用熱點的火焰圖。本質上是透過不斷的取樣,然後把收集到的取樣結果生成火焰圖。

命令基本執行結構是 profiler 命令 [命令引數]

案例

啟動profiler

$ profiler start
Started [cpu] profiling

預設情況下,生成的是cpu的火焰圖,即event為cpu。可以用--event引數來指定。

顯示支援的事件

$ profiler list

獲取已採集的sample的數量

$ profiler getSamples
23

檢視profiler狀態

$ profiler status
[cpu] profiling is running for 4 seconds

可以檢視當前profiler在取樣哪種event和取樣時間。

停止profiler

生成svg格式結果

$ profiler stop
profiler output file: /tmp/demo/arthas-output/20191125-135546.svg
OK

預設情況下,生成的結果儲存到應用的工作目錄下的arthas-output目錄。可以透過 --file引數來指定輸出結果路徑。比如:

$ profiler stop --file /tmp/output.svg
profiler output file: /tmp/output.svg
OK

生成html格式結果

預設情況下,結果檔案是svg格式,如果想生成html格式,可以用--format引數指定:

$ profiler stop --format html
profiler output file: /tmp/test/arthas-output/20191125-143329.html
OK

或者在--file引數裡用檔名指名格式。比如--file /tmp/result.html

透過瀏覽器檢視arthas-output下面的profiler結果

預設情況下,arthas使用3658埠,則可以開啟: http://localhost:3658/arthas-output/ 檢視到arthas-output目錄下面的profiler結果:

image-20200322095520034

點選可以檢視具體的結果:

image-20200322095504907

火焰圖的含義

火焰圖是基於 perf 結果產生的SVG 圖片,用來展示 CPU 的呼叫棧。

y 軸表示呼叫棧,每一層都是一個函式。呼叫棧越深,火焰就越高,頂部就是正在執行的函式,下方都是它的父函式。

x 軸表示抽樣數,如果一個函式在 x 軸佔據的寬度越寬,就表示它被抽到的次數多,即執行的時間長。注意,x 軸不代表時間,而是所有的呼叫棧合併後,按字母順序排列的。

火焰圖就是看頂層的哪個函式佔據的寬度最大。只要有"平頂"(plateaus),就表示該函式可能存在效能問題。

顏色沒有特殊含義,因為火焰圖表示的是 CPU 的繁忙程度,所以一般選擇暖色調。

小結

profiler 命令作用
profiler start 啟動profiler,預設情況下,生成cpu的火焰圖
profiler list 顯示所有支援的事件
profiler getSamples 獲取已採集的sample的數量
profiler status 檢視profiler的狀態,執行的時間
profiler stop 停止profiler,生成火焰圖的結果,指定輸出目錄和輸出格式:svg或html

Arthas實踐

需求

1. 哪個Controller處理了請求

我們可以快速定位一個請求是被哪些Filter攔截的,或者請求最終是由哪些Servlet處理的。但有時,我們想知道一個請求是被哪個Spring MVC Controller處理的。如果翻程式碼的話,會比較難找,並且不一定準確。透過Arthas可以精確定位是哪個Controller處理請求。

2. 每個請求的呼叫引數和返回值是多少

透過watch來檢視請求的引數和返回值

準備場景

將ssm_student.war專案部署到Linux的tomcat伺服器下,可以正常訪問。

啟動之後,訪問:http://192.168.254.199:8080/ssm_student ,會返回如下頁面。192.168.254.199 是Linux伺服器的地址。

那麼這個請求是被哪個Controller處理的呢?

image-20200311201333701

步驟

  1. trace定位DispatcherServlet
  2. jad反編譯DispatcherServlet
  3. watch定位handler
  4. 使用watch得到方法的入參和返回值

實現步驟

第1步:

在瀏覽器上進行登入操作,檢查最耗時的方法
trace *.DispatcherServlet *
image-20200311210304299
可以分步trace,請求最終是被DispatcherServlet#doDispatch()處理了
trace *.FrameworkServlet doService

image-20200311210115377

第2步:

trace結果裡把呼叫的行號列印出來了,我們可以直接在IDE裡檢視程式碼(也可以用jad命令反編譯)
jad --source-only *.DispatcherServlet doDispatch
image-20200311210459511

第3步:

watch *.DispatcherServlet getHandler 'returnObj'
檢視返回的結果,得到使用到了2個控制器的方法

image-20200311210600265

第4步:

watch com.itheima.controller.* * {params,returnObj} -x 2
image-20200322110452326

結論

透過trace, jad, watch最後得到這個操作由2個控制器來處理,分別是:

com.itheima.controller.UserController.login()
com.itheima.controller.StudentController.findAll()

學習總結

命令 說明
dump 將已載入類的位元組碼檔案儲存到特定的目錄中
classloader 獲取類載入器的資訊
monitor 監控指定類中方法的執行情況
watch 觀察到指定方法的呼叫情況
trace 對方法內部呼叫路徑進行追蹤,並輸出方法路徑上每個節點上耗時
stack 輸出當前方法被呼叫的路徑
tt 記錄指定方法每次呼叫的入參和返回資訊
options 全域性開關
profiler 生成火焰圖

相關文章