JVM進程診斷利器——arthas介紹

圖片看不到的話可以看我的CSDN的博客
https://blog.csdn.net/u013332124/article/details/84888074

Arthas 是Alibaba開源的Java診斷工具锨咙,采用命令行交互模式雹有,提供了豐富的功能,是排查jvm相關問題的利器犁享。

在逛github時,發(fā)現(xiàn)了這款利器塔逃,深入了解之后殿托,簡直驚為天人。下面先列舉一下它能做的一些事情:

  1. 提供性能看板冬念,包括線程、cpu牧挣、內(nèi)存等信息急前,并且會定時的刷新。
  2. 根據(jù)各種條件查看線程快照瀑构。比如找出cpu占用率最高的n個線程等
  3. 輸出jvm的各種信息裆针,如gc算法、jdk版本寺晌、ClassPath等
  4. 查看/設置sysprop和sysenv
  5. 查看某個類的靜態(tài)屬性世吨,也可以通過ognl語法執(zhí)行一些語句
  6. 查看已加載的類的詳細信息,比如這個類從哪個jar包加載的呻征。也可以查看類的方法的信息
  7. dump某個類的字節(jié)碼到指定目錄
  8. 直接反編譯指定的類
  9. 查看類加載器的一些信息
  10. 可以讓jvm重新加載某個類
  11. 監(jiān)控方法的執(zhí)行耘婚,同時可以獲取到執(zhí)行的入?yún)ⅰ⒊鰠⒁约皰伋龅漠惓?/li>
  12. 追蹤方法執(zhí)行的調(diào)用棧陆赋,以及各個方法的調(diào)用時間

下面我會對如何使用這些功能做一個簡單的介紹沐祷,也會加上一些自己對這些命令的理解嚷闭。有一些命令不會介紹的太詳細,因為官方文檔已經(jīng)寫的很棒了赖临,我沒必要再這復述胞锰。想深入了解的朋友也可以直接去看arthas的官方文檔,中文的兢榨,很容易閱讀胜蛉。

https://alibaba.github.io/arthas/install-detail.html

一、安裝和使用arthas

官方文檔:

https://alibaba.github.io/arthas/install-detail.html

安裝

直接通過`java -jar啟動:

wget https://alibaba.github.io/arthas/arthas-boot.jar
# 啟動后會自動下載響應的lib到 ~/.arthas 目錄下
java -jar arthas-boot.jar

或者直接下載arthas的壓縮包色乾,然后解壓:

unzip arthas-packaging-bin.zip
# 執(zhí)行
./as.sh

啟動asthas進程后,它會列出所有的jvm進程领突,并讓我們選擇要attach哪個進程暖璧。attach目標進程后,就進入athas的交互命令行了君旦,這時候就可以開始輸入arthas對應的命令使用了

卸載

 rm -rf ~/.arthas/

二澎办、athas的各個命令

1. dashboard

進入當前系統(tǒng)的實時數(shù)據(jù)面板,按 ctrl+c 退出金砍。這個面板會實時刷新局蚀,其中包括線程信息、內(nèi)存信息恕稠、gc信息琅绅、還有一些運行時的數(shù)據(jù)。

另外鹅巍,當運行在Ali-tomcat時千扶,會顯示當前tomcat的實時信息,如HTTP請求的qps, rt, 錯誤數(shù), 線程池信息等等骆捧。

下面是arthas上面的一張demo圖

在這里插入圖片描述

2. thread

通過thread命令可以查看當前jvm進程的線程詳情澎羞。可以查看線程的cpu使用時間占比敛苇,通過指定各種參數(shù)可以找出最忙的幾個線程妆绞,以及阻塞其他線程的線程。具體如何使用這里不多做介紹枫攀,大家可以去看arthas的官方文檔括饶。

3. jvm

通過jvm命令直接輸出當前jvm的各種信息。

4. sysprop和sysenv

通過sysprop可以查看所有的系統(tǒng)變量脓豪,也可以設置某個系統(tǒng)變量巷帝。

同理,通過sysenv可以查看所有的操作系統(tǒng)環(huán)境變量扫夜,也可以查看設置某個環(huán)境變量楞泼。

5. getstatic

通過該命令可以查看類的靜態(tài)屬性驰徊。不過查看類的靜態(tài)屬性ognl命令也可以做到,官方也比較推薦使用ognl表達式來做堕阔。

不過使用getstatic可以使用通配符來查看變量棍厂,好像用ognl不行。(也可能是我對ognl表達式了解還不夠)

# 查看CommonConstants類下的所有靜態(tài)屬性
getstatic *.CommonConstants *

6. ognl

通過ognl表達式來執(zhí)行一些語句超陆。

# 調(diào)用靜態(tài)函數(shù)
ognl '@java.lang.System@out.println("hello")'
# 輸出
null

# 獲取靜態(tài)類的靜態(tài)字段
ognl '@demo.MathGame@random'
# 輸出
@Random[
    serialVersionUID=@Long[3905348978240129619],
    seed=@AtomicLong[125451474443703],
    multiplier=@Long[25214903917],
    addend=@Long[11],
    mask=@Long[281474976710655],
    DOUBLE_UNIT=@Double[1.1102230246251565E-16],
    BadBound=@String[bound must be positive],
    BadRange=@String[bound must be greater than origin],
    BadSize=@String[size must be non-negative],
    seedUniquifier=@AtomicLong[-3282039941672302964],
    nextNextGaussian=@Double[0.0],
    haveNextNextGaussian=@Boolean[false],
    serialPersistentFields=@ObjectStreamField[][isEmpty=false;size=3],
    unsafe=@Unsafe[sun.misc.Unsafe@28ea5898],
    seedOffset=@Long[24],
]

# 執(zhí)行多行表達式牺弹,賦值給臨時變量,返回一個List
ognl '#value1=@System@getProperty("java.home"),#value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
# 輸出
@ArrayList[
    @String[/opt/java/8.0.181-zulu/jre],
    @String[OpenJDK Runtime Environment],
]

ognl表達式在arthas中用的還是比較多的时呀,語法也比較簡單张漂。在后面的monitor、watch谨娜、trace航攒、stack等命令中都會排上用場。

關于ognl趴梢,這個userCase上有豐富的案例:

https://github.com/alibaba/arthas/issues/11

7. sc 和 sm

通過sc可以查看已加載類的相關信息漠畜,比如該類是從哪個jar包加載的,被哪個類加載器加載的坞靶,以及是否是接口等等憔狞。

sm查看已加載類的方法詳情。

8. dump

將已加載類的字節(jié)碼dump到本地磁盤上彰阴。

9. jad

反編譯已加載的類瘾敢。讓它變成可讀的形式。

有時我們經(jīng)常會不確定線上或者測試環(huán)境的包是否是我們修改過的硝枉,這時候就可以通過jad反編譯來看下廉丽。

10. classloader

將 JVM 中所有的classloader的信息統(tǒng)計出來,并可以展示繼承樹妻味,urls等正压。

11. redefine

該命令可以加載外部的.class文件,然后覆蓋 jvm已加載的類责球。注意焦履,這個命令不一定都能覆蓋成功,如果添加了新的field雏逾,就不會加載成功嘉裤。

關于redefine,arthas的github上有個非常經(jīng)典的userCase:

https://github.com/alibaba/arthas/issues/263

大體就是作者遇到項目中的日志一直輸出[] [] [] No credential found栖博,想要找到是哪個類輸出的屑宠。由于大多數(shù)日志框架輸出日志時都用到了StringBuilder,因此作者對StringBuilder的toString方法做了以下修改:

    @Override
    public String toString() {
        // Create a copy, don't share the array
        String result = new String(value, 0, count);
        if(result.contains("No credential found")) {
            System.err.println(result);
            new Throwable().printStackTrace();
        }
        return result;
    }

改完類之后再用redefine把修改后的StringBuilder加載進去仇让,這樣典奉,當后面繼續(xù)輸出[] [] [] No credential found就可以知道到底是從哪里輸出的了躺翻。

12. monitor

monitor命令可以監(jiān)控方法的執(zhí)行情況。比如調(diào)用成功次數(shù)卫玖,失敗次數(shù)公你,失敗率、平均執(zhí)行時間等等假瞬。默認120秒輸出一次陕靠,也就是說,當我們輸入monitor命令之后脱茉,每120秒就會輸出一次統(tǒng)計結果剪芥。

通過-c參數(shù)可以修改輸出頻率,支持通配符和正則表達式琴许。

13. watch

讓你能方便的觀察到指定方法的調(diào)用情況粗俱。能觀察到的范圍為:返回值拋出異常虚吟、入?yún)?/code>,通過編寫 OGNL 表達式進行對應變量的查看签财。

watch的使用姿勢比較豐富串慰,可以在四個不同的場景觀察方法的執(zhí)行。比如方法調(diào)用之前唱蒸、方法調(diào)用之后邦鲫、方法異常之后方法結束之后神汹。默認觀察的是方法結束之后庆捺。

如果觀察的是方法結束之后的場景,由于入?yún)⒖赡茉趫?zhí)行方法時被改變屁魏,所以此時輸出的可能不是真正的入?yún)⑻弦浴R虼耍凑嬲娜雲(yún)⒚テ矗捶椒ㄕ{(diào)用之前的你画,也就是加上-b的參數(shù)。

另外桃漾,使用-b參數(shù)觀察的話坏匪,則觀察不到方法返回的結果以及拋出的異常了。

相關參數(shù):

參數(shù)名稱 參數(shù)說明
class-pattern 類名表達式匹配
method-pattern 方法名表達式匹配
express 觀察表達式
condition-express 條件表達式
[b] 方法調(diào)用之前觀察
[e] 方法異常之后觀察
[s] 方法返回之后觀察
[f] 方法結束之后(正常返回和異常返回)觀察
[E] 開啟正則表達式匹配撬统,默認為通配符匹配
[x:] 指定輸出結果的屬性遍歷深度适滓,默認為 1
[n:] 只執(zhí)行n次,默認會不斷輸出恋追,除非用戶按下cltr+c
# 觀察CommonTest的test方法
# 輸出 入?yún)⑵炯!⒎祷亟Y果罚屋、拋出的異常 —— 輸出的內(nèi)容可以動態(tài)調(diào)整
# 后面跟著的是 條件表達式,表示耗時超過10ms才輸出
# -n 表示只執(zhí)行一次蕊苗,-x表示 入?yún)⒑头祷亟Y果的展開層次為5層
watch *.CommonTest test "{params,returnObj,throwExp}" '#cost>10' -x 5 -n 1

# 耗時大于10ms并且第一個參數(shù)等于1才輸出
watch *.CommonTest test "{params,returnObj,throwExp}" '#cost>10 && params[0]==1' -x 5 -n 1
# 第一個參數(shù)大于1 并且第二個參數(shù)等于hello才輸出
watch *.CommonTest test "{params,returnObj,throwExp}" 'params[0]>1 && params[1]=="hello"' -x 5 -n 1
# 第一個參數(shù)小于5或者第二個參數(shù)等于"world"就輸出
watch *.CommonTest test "{params,returnObj,throwExp}" 'params[0]<5 || params[1]=="wolrd"' -x 5 -n 1
# 第一個參數(shù)的name字段等于world時才輸出沿后。
# 由于在方法執(zhí)行過程中參數(shù)的name屬性可能發(fā)生改變,因此加上-b才能觀察到真正的入?yún)?watch -b *.CommonTest test "{params,returnObj,throwExp}" 'params[0].name=="wolrd"' -x 5 -n 1

# 由于同時指定了-s和-b朽砰,所以方法被調(diào)用一次尖滚,就會輸出2次結果(兩個場景分開輸出),分別是方法被調(diào)用前瞧柔,和返回之后
# 注意漆弄,這里如果-n只設置成1,那么只會輸出-b對應的輸出,-s對應的輸出由于沒有次數(shù)了就無法輸出了
watch *.CommonTest test '{params,returnObj,throwExp}' -x 5 -n 2 -s -b

在填寫條件表達式時要注意一點造锅,條件表達式中的params默認都是獲取的方法執(zhí)行完后的參數(shù)信息撼唾,比如入?yún)的屬性name方法執(zhí)行前是"hello",在方法執(zhí)行后變成了"world",那么條件表達式傳入'params[0].name="hello"'將不會輸出,只有填入'params[0].name="hello"'才可以匹配上哥蔚。這點對于后面的trace倒谷、stack命令也是一樣的。

14. trace

方法內(nèi)部調(diào)用路徑糙箍,并輸出方法路徑上的每個節(jié)點上耗時渤愁。但是該命令只能輸出一級調(diào)用的方法耗時,往下的就不會輸出了深夯。比如我定義了一個類Test

public class Test{
    
    public void a(){
        b();
    }
    
    public void b(){
        c();
    }
    
    public void c(){
        //...
    }
}

當我要觀察Test a時抖格,trace命令只會輸出b()的耗時,而不會輸出c()的耗時咕晋。因為對方法a來說雹拄,只有b()是一級調(diào)用。

trace命令也可以使用條件表達式掌呜,來過濾一些不想要的輸出

# 方法耗時大于100時才輸出滓玖,且只輸出1.其他條件過濾的語法可以看watch命令的demo
trace *.Common* test '#cost>100' -n 1

友情提醒下,trace 在執(zhí)行的過程中本身是會有一定的性能開銷质蕉,在統(tǒng)計的報告中并未像 JProfiler 一樣預先減去其自身的統(tǒng)計開銷呢撞。所以這統(tǒng)計出來有些許的不準,渲染路徑上調(diào)用的類饰剥、方法越多殊霞,性能偏差越大。但還是能讓你看清一些事情的汰蓉。

這里存在一個統(tǒng)計不準確的問題绷蹲,就是所有方法耗時加起來可能會小于該監(jiān)測方法的總耗時,這個是由于 Arthas 本身的邏輯會有一定的耗時

15. stack

輸出當前方法被調(diào)用的調(diào)用路徑。用法和trace差不多

# 方法耗時大于100時才輸出祝钢,且只輸出1.其他條件過濾的語法可以看watch命令的demo
stack *.Common* test '#cost>100' -n 1

16. tt

tt命令會記錄每次方法調(diào)用的各種信息比规。它和watch有些相似但是它能記錄下各個時間點的調(diào)用信息,之后隨時查看拦英,甚至replay這次調(diào)用蜒什。

在這里插入圖片描述

從上圖中我們可以看到,通過-i參數(shù)我們可以直接查看之前某次調(diào)用的詳細信息疤估。

# 指定第一個參數(shù)的mobile字段等于13989838402
tt -t *.CommonTest test params[0].mobile=="13989838402"
# 把之前記錄的那些調(diào)用都輸出來
tt -l
# 

replay某次調(diào)用:

# replay時間片的index=1002這次調(diào)用
tt -i 1002 -p

使用tt進行方法replay時灾常,要注意2點

  1. ThreadLocal 信息丟失

    很多框架偷偷的將一些環(huán)境變量信息塞到了發(fā)起調(diào)用線程的 ThreadLocal 中,由于調(diào)用線程發(fā)生了變化铃拇,這些 ThreadLocal 線程信息無法通過 Arthas 保存钞瀑,所以這些信息將會丟失。

    一些常見的 CASE 比如:鷹眼的 TraceId 等慷荔。

  2. 引用的對象

    需要強調(diào)的是雕什,tt 命令是將當前環(huán)境的對象引用保存起來,但僅僅也只能保存一個引用而已显晶。如果方法內(nèi)部對入?yún)⑦M行了變更贷岸,或者返回的對象經(jīng)過了后續(xù)的處理,那么在 tt 查看的時候將無法看到當時最準確的值磷雇。這也是為什么 watch 命令存在的意義凰盔。

17. options

options用來打開關閉某些功能:

名稱 默認值 描述
unsafe false 是否支持對系統(tǒng)級別的類進行增強,打開該開關可能導致把JVM搞掛倦春,請慎重選擇!
dump false 是否支持被增強了的類dump到外部文件中落剪,如果打開開關睁本,class文件會被dump到/${application dir}/arthas-class-dump/目錄下,具體位置詳見控制臺輸出
batch-re-transform true 是否支持批量對匹配到的類執(zhí)行retransform操作
json-format false 是否支持json化的輸出
disable-sub-class false 是否禁用子類匹配忠怖,默認在匹配目標類的時候會默認匹配到其子類呢堰,如果想精確匹配,可以關閉此開關
debug-for-asm false 打印ASM相關的調(diào)試信息
save-result false 是否打開執(zhí)行結果存日志功能凡泣,打開之后所有命令的運行結果都將保存到/home/admin/logs/arthas/arthas.log
job-timeout 1d 異步后臺任務的默認超時時間枉疼,超過這個時間,任務自動停止鞋拟;比如設置 1d, 2h, 3m, 25s骂维,分別代表天、小時贺纲、分航闺、秒
$ options save-result true                                                                                         
 NAME         BEFORE-VALUE  AFTER-VALUE                                                                            
----------------------------------------                                                                           
 save-result  false         true

18. help

通過help命令可以查看某個命令的使用詳情

help tt
help stack
...

三、遇到的一些問題

在使用arthas的過程中,會遇到一些問題潦刃,這里做個記錄侮措。

1. attach錯進程

在我們要去sc、jad某個類乖杠,發(fā)現(xiàn)arthas提示類沒加載分扎,但是我們很確定類已經(jīng)加載了。這時候就要檢查arthas attach的進程是不是我們目標的進程胧洒。在進入arthas交互界面時會輸出一些信息畏吓,里面就有真正attach的進程id:

在這里插入圖片描述

在上圖我們可以看到,明明我選擇的是要attach 37179這個進程略荡,但實際arthas attach的是26792這個進程庵佣。這是由于我們之前用arthas attach過26792這個進程,并且沒有執(zhí)行 shutdown命令導致的汛兜。也就是說執(zhí)行exit或者quit只會退出交互界面巴粪,不會關閉attach的arthas進程。

解決辦法

再次進入arthas交互界面粥谬,執(zhí)行 shutdown 命令肛根,之后重新attach新的進程即可。

四漏策、總結

在以前派哲,我們排查線上jvm進程時,用的命令無非就是jps掺喻、jstack芭届、jmap、jstat感耙、jhat這些jvm命令褂乍。但是這些命令能做的事都比較有限。有了arthas即硼,我們可以在線上進行更加豐富的操作逃片,對我們排查問題有很大的幫助。

另外只酥,想學習arthas的話褥实,建議不要只看文檔,最好自己手動把所有命令都試一遍裂允,印象會比較深刻损离,下次遇到類似的問題也可以及時想到這個工具。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绝编,一起剝皮案震驚了整個濱河市草冈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖怎棱,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哩俭,死亡現(xiàn)場離奇詭異,居然都是意外死亡拳恋,警方通過查閱死者的電腦和手機凡资,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谬运,“玉大人隙赁,你說我怎么就攤上這事“鹋” “怎么了伞访?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長轰驳。 經(jīng)常有香客問我厚掷,道長,這世上最難降的妖魔是什么级解? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任冒黑,我火速辦了婚禮,結果婚禮上勤哗,老公的妹妹穿的比我還像新娘抡爹。我一直安慰自己,他們只是感情好芒划,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布冬竟。 她就那樣靜靜地躺著,像睡著了一般民逼。 火紅的嫁衣襯著肌膚如雪泵殴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天缴挖,我揣著相機與錄音,去河邊找鬼焚辅。 笑死映屋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的同蜻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叁鉴!你這毒婦竟也來了壹甥?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贬循,沒想到半個月后咸包,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡杖虾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年烂瘫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奇适。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡坟比,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嚷往,到底是詐尸還是另有隱情葛账,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布皮仁,位于F島的核電站籍琳,受9級特大地震影響,放射性物質發(fā)生泄漏魂贬。R本人自食惡果不足惜巩割,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望付燥。 院中可真熱鬧宣谈,春花似錦、人聲如沸键科。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽勋颖。三九已至嗦嗡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饭玲,已是汗流浹背侥祭。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茄厘,地道東北人矮冬。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像次哈,于是被迫代替她去往敵國和親胎署。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容