Arthas-阿爾薩斯
Arthas(阿爾薩斯)是阿里巴巴開源的 Java 診斷工具,深受開發者喜愛。
當你遇到以下類似問題而束手無策時,Arthas 可以幫助你解決:
這個類從哪個 jar 包加載的?為什么會報各種類相關的 Exception?
我改的代碼為什么沒有執行到?難道是我沒 commit?分支搞錯了?
遇到問題無法在線上 debug,難道只能通過加日志再重新發布嗎?
線上遇到某個用戶的數據處理有問題,但線上同樣無法 debug,線下無法重現!
是否有一個全局視角來查看系統的運行狀況?
有什么辦法可以監控到JVM的實時運行狀態?
Arthas 采用命令行交互模式,同時提供豐富的 Tab 自動補全功能,進一步方便進行問題的定位和診斷。
計划
Arthas 【阿爾薩斯】
Alibaba開源的Java診斷工具。
使用教程:https://www.jianshu.com/p/507f7e0cc3a3
官網:https://arthas.aliyun.com/doc/ 【官方的文檔超級詳細】
很厲害的樣子,o( ̄▽ ̄)d
去試玩一下 罒ω罒
問題:不支持openjdk,因為其沒有jps,arthas是用jps去找java進程的,需要使用oracle jdk
后面的Jstack等參考:https://blog.csdn.net/zbajie001/article/details/80045710
文章目錄
Arthas-阿爾薩斯
計划
@[toc]
開啟使用
dashboard 顯示實時數據面板
thread - jvm線程信息
jvm - 【略】
基礎命令
各種系統信息
ognl - 查看靜態類等
sc - 用來查詢JVM已加載的類信息
sm - 查看已加載類的方法信息
jad - 反編譯指定已加載類的源碼
monitor - 方法執行監控
watch- 方法執行數據觀測
trace - 方法內部調用路徑
stack - 方法被調用的調用路徑
tt - 執行方法觀測
重做一次調用
小結
關於Jstack - 查看堆棧信息
試試
先找找這東西在哪里
看看java項目的進程:
查看下對應線程的情況
最后查看堆棧信息
關於Jmap-內存分配【略】
關於Jstat-資源性能監控【實用】
試試
再小結一下
###嘗試-快速安裝使用
authas是一個jar包,可以直接linux下載后運行:
wget https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
java -jar arthas-boot.jar -h # 打印幫助信息
就可以啟動起來。啟動后,authas會自動檢測存在的java進程,這時候需要選擇你想要診斷的進程,回車即可。
開啟使用
[root@chuangshoubaoceshi01 arthas]# java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.4.4
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 2262 jenkins.war
[2]: 13819 admin-console-1.1.jar
[3]: 3103 chuangke-api.jar
2
[INFO] Start download arthas from remote server: https://arthas.aliyun.com/download/3.4.4?mirror=aliyun
[INFO] File size: 11.94 MB, downloaded size: 2.56 MB, downloading ...
[INFO] File size: 11.94 MB, downloaded size: 5.60 MB, downloading ...
[INFO] File size: 11.94 MB, downloaded size: 8.58 MB, downloading ...
[INFO] File size: 11.94 MB, downloaded size: 11.62 MB, downloading ...
[INFO] Download arthas success.
[INFO] arthas home: /root/.arthas/lib/3.4.4/arthas
[INFO] Try to attach process 13819
[INFO] Attach process 13819 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
[arthas@13819]$ dashboard
dashboard 顯示實時數據面板
參數名稱 參數說明
[i:] 刷新實時數據的時間間隔 (ms),默認5000ms
[n:] 刷新實時數據的次數
數據說明
- ID: Java級別的線程ID,注意這個ID不能跟jstack中的nativeID一一對應。
- NAME: 線程名
- GROUP: 線程組名
- PRIORITY: 線程優先級, 1~10之間的數字,越大表示優先級越高
- STATE: 線程的狀態
- CPU%: 線程的cpu使用率。比如采樣間隔1000ms,某個線程的增量cpu時間為100ms,則cpu使用率=100/1000=10%
- DELTA_TIME: 上次采樣之后線程運行增量CPU時間,數據格式為秒
- TIME: 線程運行總CPU時間,數據格式為分:秒
- INTERRUPTED: 線程當前的中斷位狀態
- DAEMON: 是否是daemon線程
可以看到,這里會顯示出線程(按照cpu占用百分比倒排)、內存(堆空間實時情況)、GC情況等數據。
thread - jvm線程信息
常用情況:
- thread 22 【22為線程id】 將會打印線程運行堆棧信息。
- thread --state WAITING 【查看指定狀態的線程】
- thread -b 【找出當前阻塞其他線程的線程】
- thread -n 3 -i 1000 【列出1000ms內最忙的3個線程棧】
jvm - 【略】
基礎命令
- help——查看命令幫助信息
- cat——打印文件內容,和linux里的cat命令類似
- pwd——返回當前的工作目錄,和linux命令類似
- cls——清空當前屏幕區域
- session——查看當前會話的信息
- reset——重置增強類,將被 Arthas 增強過的類全部還原,Arthas 服務端關閉時會重置所有增強過的類
- version——輸出當前目標 Java 進程所加載的 Arthas 版本號
- history——打印命令歷史
- quit——退出當前 Arthas 客戶端,其他 Arthas 客戶端不受影響
- shutdown——關閉 Arthas 服務端,所有 Arthas 客戶端全部退出
- keymap——Arthas快捷鍵列表及自定義快捷鍵
各種系統信息
- sysprop - 查看當前JVM的系統屬性
- sysenv - 查看當前JVM的環境屬性
- vmoption - 查看,更新VM診斷相關的參數
- perfcounter - 查看當前JVM的 Perf Counter信息【性能計數器】
- classloader - 查看classloader的繼承樹,urls,類加載信息
ognl - 查看靜態類等
sc - 用來查詢JVM已加載的類信息
- [arthas@13819]$ sc *.*User
- com.ibeetl.admin.core.entity.CoreUser
- com.ibeetl.admin.core.entity.User
- com.ibeetl.app.entity.AppPushUser
- com.ibeetl.cms.entity.User
- com.ibeetl.cms.web.vo.UserVO
- org.apache.xmlbeans.impl.values.JavaIntegerHolder
- org.apache.xmlbeans.impl.values.JavaStringHolder
- org.apache.xmlbeans.impl.values.TypeStoreUser
- org.apache.xmlbeans.impl.values.XmlIntegerImpl
- org.apache.xmlbeans.impl.values.XmlObjectBase
- org.apache.xmlbeans.impl.values.XmlStringImpl
- org.springframework.boot.autoconfigure.security.SecurityProperties$User
- Affect(row-cnt:12) cost in 77 ms.
- sc -d com.ibeetl.admin.core.entity.CoreUser 【查看更具體點的信息】
sm - 查看已加載類的方法信息
查看類的所有方法 -d 可查看參數信息等
- [arthas@13819]$ sm com.ibeetl.admin.core.entity.CoreUser
- com.ibeetl.admin.core.entity.CoreUser <init>()V
- com.ibeetl.admin.core.entity.CoreUser setState(Ljava/lang/String;)V
- com.ibeetl.admin.core.entity.CoreUser getPassword()Ljava/lang/String;
- 。。。。。
- Affect(row-cnt:25) cost in 14 ms.
jad - 反編譯指定已加載類的源碼
- [arthas@13819]$ jad com.ibeetl.admin.core.entity.CoreUser
- ClassLoader:
- +-org.springframework.boot.loader.LaunchedURLClassLoader@3f91beef
- +-sun.misc.Launcher$AppClassLoader@5c647e05
- +-sun.misc.Launcher$ExtClassLoader@5a10411
- Location:
- file:/u01/csb/sys/admin-console-1.1.jar!/BOOT-INF/lib/admin-core-1.1.jar!/
- package com.ibeetl.admin.core.entity;
- import com.fasterxml.jackson.annotation.JsonIgnore;
- import com.ibeetl.admin.core.annotation.Dict;
- 。。。。。
- public class CoreUser
- extends BaseEntity {
- 。。。。。。
monitor - 方法執行監控
對匹配 class-pattern/method-pattern的類、方法的調用進行監控。直到用戶輸入 Ctrl+C 為止。
- [arthas@13819]$ monitor -c 5 com.ibeetl.admin.core.entity.CoreUser
- The argument 'method-pattern' is required, description: Method of Pattern Matching
- [arthas@13819]$ monitor -c 5 com.ibeetl.admin.core.entity.CoreUser
- [arthas@13819]$ monitor -c 5 com.ibeetl.admin.core.entity.CoreUser getPassword
- Press Q or Ctrl+C to abort.
- Affect(class count: 1 , method count: 1) cost in 137 ms, listenerId: 1
- timestamp class method total success fail avg-rt(ms) fail-rate
- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 2020-11-24 15:36:57 com.ibeetl.admin.core.entity.CoreUser getPassword 1 1 0 0.64 0.00%
確實可以看到,基本事實的調用情況,這里統計周期為5秒。
watch- 方法執行數據觀測
查看函數的參數、返回值、異常信息,如果有請求觸發,就回打印對應的數據。
這個方法參數好多,可以監控方法各個情況的數據。
如過需要按照耗時進行過濾,需要加入: ‘#cost>200’ 代表耗時超過200ms的才會打印出來。
- [arthas@13819]$ watch com.ibeetl.admin.core.entity.CoreUser getPassword
- Press Q or Ctrl+C to abort.
- Affect(class count: 1 , method count: 1) cost in 53 ms, listenerId: 2
- ts=2020-11-24 15:45:46; [cost=0.056363ms] result=@ArrayList[
- @Object[][isEmpty=true;size=0],
- @CoreUser[com.ibeetl.admin.core.entity.CoreUser@70f4eb5a],
- @String[123456],
trace - 方法內部調用路徑
方法內部調用路徑,並輸出方法路徑上的每個節點上耗時
trace 命令能主動搜索 class-pattern/method-pattern 對應的方法調用路徑,渲染和統計整個調用鏈路上的所有性能開銷和追蹤調用鏈路。
- [arthas@13819]$ trace com.ibeetl.admin.core.entity.CoreUser getPassword
- Press Q or Ctrl+C to abort.
- Affect(class count: 1 , method count: 1) cost in 58 ms, listenerId: 3
- `---ts=2020-11-24 15:52:15;thread_name=http-nio-8111-exec-7;id=1c;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@19beff11
- `---[0.05302ms] com.ibeetl.admin.core.entity.CoreUser:getPassword()
- # 可以看到,調用這個的方法是最后這個 ↑↑↑↑↑↑↑↑↑
使用-j (忽略jdk method trace)、’#cost>10’(過濾耗時時間) -n (執行次數)
stack - 方法被調用的調用路徑
很多時候我們都知道一個方法被執行,但這個方法被執行的路徑非常多,或者你根本就不知道這個方法是從那里被執行了,此時你需要的是 stack 命令。
輸出當前方法被調用的調用路徑,相當的完整,從線程run開始的,所有調用路徑,o( ̄▽ ̄)d
- [arthas@13819]$ stack com.ibeetl.admin.core.entity.CoreUser getPassword -n 1
- Press Q or Ctrl+C to abort.
- Affect(class count: 1 , method count: 1) cost in 51 ms, listenerId: 5
- ts=2020-11-24 15:58:19;thread_name=http-nio-8111-exec-9;id=1e;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@19beff11
- @com.ibeetl.admin.core.entity.CoreUser.getPassword()
- at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- 。。。。。。。。
- at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
- at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
- at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
- at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
- at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
- at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
- at java.lang.Thread.run(Thread.java:745)
- # 太多了,中間就略過了
tt - 執行方法觀測
方法執行數據的時空隧道,記錄下指定方法每次調用的入參和返回信息,並能對這些不同的時間下調用進行觀測
watch 雖然很方便和靈活,但需要提前想清楚觀察表達式的拼寫,這對排查問題而言要求太高,因為很多時候我們並不清楚問題出自於何方,只能靠蛛絲馬跡進行猜測。
這個時候如果能記錄下當時方法調用的所有入參和返回值、拋出的異常會對整個問題的思考與判斷非常有幫助。
- [arthas@13819]$ tt com.ibeetl.admin.core.entity.CoreUser getPassword -t -n 3
- Press Q or Ctrl+C to abort.
- Affect(class count: 1 , method count: 1) cost in 58 ms, listenerId: 7
- INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 1000 2020-11-24 17:15:48 0.092734 true false 0x44e5c9a1 CoreUser getPassword
- 1001 2020-11-24 17:15:53 0.02218 true false 0x322b3578 CoreUser getPassword
- 1002 2020-11-24 17:15:56 0.028293 true false 0x4e46b99c CoreUser getPassword
- Command execution times exceed limit: 3, so command will exit. You can set it with -n option.
重做一次調用
當你稍稍做了一些調整之后,你可能需要前端系統重新觸發一次你的調用,此時得求爺爺告奶奶的需要前端配合聯調的同學再次發起一次調用。而有些場景下,這個調用不是這么好觸發的。
- tt -s ‘method.name==“primeFactors”’ : 選出 primeFactors 方法的調用信息
- # 顯示歷史
- [arthas@13819]$ tt -l
- INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 1000 2020-11-24 17:15:48 0.092734 true false 0x44e5c9a1 CoreUser getPassword
- 1001 2020-11-24 17:15:53 0.02218 true false 0x322b3578 CoreUser getPassword
- 1002 2020-11-24 17:15:56 0.028293 true false 0x4e46b99c CoreUser getPassword
- Affect(row-cnt:3) cost in 1 ms.
- # 查詢與重做
- [arthas@13819]$ tt -i 1000
- INDEX 1000
- GMT-CREATE 2020-11-24 17:15:48
- COST(ms) 0.092734
- OBJECT 0x44e5c9a1
- CLASS com.ibeetl.admin.core.entity.CoreUser
- METHOD getPassword
- IS-RETURN true
- IS-EXCEPTION false
- RETURN-OBJ @String[123456]
- Affect(row-cnt:1) cost in 0 ms.
- [arthas@13819]$ tt -i 1000 -p
- RE-INDEX 1000
- GMT-REPLAY 2020-11-24 17:23:23
- OBJECT 0x44e5c9a1
- CLASS com.ibeetl.admin.core.entity.CoreUser
- METHOD getPassword
- IS-RETURN true
- IS-EXCEPTION false
- COST(ms) 0.123085
- RETURN-OBJ @String[123456]
- Time fragment[1000] successfully replayed 1 times.
小結
官方的文檔灰常的詳細實用,上面的方法,比較的常用,並且只是簡單的試了一下。
還有好多的參數玩法是要去看官方文檔再去深入玩耍的!!!
而關於這個Arthas不支持open jdk 的問題。好像沒法解決啊! 只能去看看另一個工具:Jstack 的了。
關於Arthas Tunnel 這個的遠程管理,就有需要再看了。畢竟分布集群的情況下才會用到。
2020-11 小杭 ヽ( ̄ω ̄( ̄ω ̄〃)ゝ
關於Jstack - 查看堆棧信息
- jstack能得到運行java程序的java stack和native stack的信息。【堆棧信息】
試試
先找找這東西在哪里
- [root@localhost bin]# find / -name jstack
- /usr/local/java/jdk1.8.0_51/bin/jstack
- [root@localhost bin]# ./jstack -h
- Usage:
- jstack [-l] <pid>
- (to connect to running process)
- jstack -F [-m] [-l] <pid>
- (to connect to a hung process)
- jstack [-m] [-l] <executable> <core>
- (to connect to a core file)
- jstack [-m] [-l] [server_id@]<remote server IP or hostname>
- (to connect to a remote debug server)
- Options:
- -F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
- -m to print both java and native frames (mixed mode)
- -l long listing. Prints additional information about locks
- -h or -help to print this help message
看看java項目的進程:
- [root@localhost bin]# top
- top - 10:04:56 up 68 days, 28 min, 1 user, load average: 0.04, 0.12, 0.22
- Tasks: 117 total, 1 running, 116 sleeping, 0 stopped, 0 zombie
- %Cpu(s): 0.3 us, 0.2 sy, 0.0 ni, 99.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
- KiB Mem : 8010580 total, 1584152 free, 4268572 used, 2157856 buff/cache
- KiB Swap: 4063228 total, 4015568 free, 47660 used. 3350348 avail Mem
- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
- 19715 root 20 0 5773368 1.048g 14280 S 1.3 13.7 1:23.82 java
- 17985 root 20 0 6081164 816336 14492 S 0.7 10.2 14:34.59 java
- 2258 root 20 0 5760104 679220 5012 S 0.3 8.5 116:37.37 java
- 1 root 20 0 190800 2956 2032 S 0.0 0.0 0:15.68 systemd
- 2 root 20 0 0 0 0 S 0.0 0.0 0:01.04 kthreadd
- 3 root 20 0 0 0 0 S 0.0 0.0 0:00.97 ksoftirqd/0
- 5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
- 7 root rt 0 0 0 0 S 0.0 0.0 0:00.02 migration/0
- # ps -ef | grep java 用這個也是可以找到要的java進程的
查看下對應線程的情況
- [root@localhost bin]# top -Hp 17985
- top - 10:23:49 up 68 days, 47 min, 1 user, load average: 0.00, 0.08, 0.18
- Threads: 84 total, 0 running, 84 sleeping, 0 stopped, 0 zombie
- %Cpu(s): 0.3 us, 0.2 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
- KiB Mem : 8010580 total, 1591992 free, 4267688 used, 2150900 buff/cache
- KiB Swap: 4063228 total, 4015568 free, 47660 used. 3351260 avail Mem
- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
- 18076 root 20 0 6081164 817720 14492 S 1.0 10.2 8:23.61 java
- 18000 root 20 0 6081164 817720 14492 S 0.3 10.2 0:17.44 java
- 18075 root 20 0 6081164 817720 14492 S 0.3 10.2 0:11.31 java
- 17985 root 20 0 6081164 817720 14492 S 0.0 10.2 0:00.00 java
- 17987 root 20 0 6081164 817720 14492 S 0.0 10.2 0:24.73 java
- 17988 root 20 0 6081164 817720 14492 S 0.0 10.2 0:05.27 java
- # 可以看到 cpu消耗最大的線程
- # printf "%x\n" 18076
- # 469c 這個是nid,線程編號了 16進制的
最后查看堆棧信息
因為信息會很多,還是把查到的數據弄到文件吧,方便后面查找
- [root@localhost bin]# ./jstack 17985 > ./test.log
- [root@localhost bin]# vim ./test.log
- /nid=0x469c # 用這個vim的查找,就可以查到對應堆棧信息了
- # 數據大概這個樣子:
- "sentinel-time-tick-thread" #24 daemon prio=5 os_prio=0 tid=0x00007fa46c079000 nid=0x469c sleeping[0x00007fa4c13d1000]
- java.lang.Thread.State: TIMED_WAITING (sleeping)
- at java.lang.Thread.sleep(Native Method)
- at java.lang.Thread.sleep(Thread.java:340)
- at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
- at com.alibaba.csp.sentinel.util.TimeUtil$1.run(TimeUtil.java:37)
- at java.lang.Thread.run(Thread.java:745)
關於Jmap-內存分配【略】
得到運行java程序的內存分配的詳細情況。這個有需要再說吧,操作太虎咋了╮(╯_╰)╭
關於Jstat-資源性能監控【實用】
這是一個比較實用的一個命令,可以觀察到classloader,compiler,gc相關信息。可以時時監控資源和性能
官方文檔:https://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstat.html
試試
- [root@localhost bin]# ./jstat -gc 17985 100 5
- S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
- 512.0 512.0 298.1 0.0 39424.0 6645.5 263168.0 173533.1 126040.0 119185.4 13912.0 12737.1 1786 9.516 4 0.833 10.350
- 512.0 512.0 298.1 0.0 39424.0 6645.5 263168.0 173533.1 126040.0 119185.4 13912.0 12737.1 1786 9.516 4 0.833 10.350
- 512.0 512.0 298.1 0.0 39424.0 6645.5 263168.0 173533.1 126040.0 119185.4 13912.0 12737.1 1786 9.516 4 0.833 10.350
- 512.0 512.0 298.1 0.0 39424.0 6645.5 263168.0 173533.1 126040.0 119185.4 13912.0 12737.1 1786 9.516 4 0.833 10.350
- 512.0 512.0 298.1 0.0 39424.0 6645.5 263168.0 173533.1 126040.0 119185.4 13912.0 12737.1 1786 9.516 4 0.833 10.350
- # 命令解釋 jstat [參數] pid [采樣間隔默認單位是毫秒] [樣本數]
具體各個參數的解釋請跳轉查看官方文檔。
給個可以查的東東有哪些:
- -class:統計class loader行為信息
- -compile:統計編譯行為信息
- -gc:統計jdk gc時heap信息
- -gccapacity:統計不同的generations(不知道怎么翻譯好,包括新生區,老年區,permanent區)相應的heap容量情況
- -gccause:統計gc的情況,(同-gcutil)和引起gc的事件
- -gcnew:統計gc時,新生代的情況
- -gcnewcapacity:統計gc時,新生代heap容量
- -gcold:統計gc時,老年區的情況
- -gcoldcapacity:統計gc時,老年區heap容量
- -gcpermcapacity:統計gc時,permanent區heap容量
- -gcutil:統計gc時,heap情況
再小結一下
這幾個命令是jdk自帶的,open jdk 和oracle jdk 都有的,所有還是需要知道一下的。
關於使用的話,可以簡單的查看jvm使用情況,以及線程出問題的排查。
用於項目正式環境排查異常還是挺方便的,如果用不了arthas的情況下。
【簡單查看jvm信息,這個比arthas還要簡單的。裝一下還是很方便的。