Alibaba Java診斷工具Arthas簡單介紹 :
當你遇到以下類似問題而束手無策時,Arthas可以幫助你解決:
1.這個類從哪個 jar 包加載的?為什么會報各種類相關的 Exception?
2.我改的代碼為什么沒有執行到?難道是我沒 commit?分支搞錯了?
3.遇到問題無法在線上 debug,難道只能通過加日志再重新發布嗎?
4.線上遇到某個用戶的數據處理有問題,但線上同樣無法 debug,線下無法重現!
5.是否有一個全局視角來查看系統的運行狀況?
6.有什么辦法可以監控到JVM的實時運行狀態?
Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同時提供豐富的 Tab 自動補全功能,進一步方便進行問題的定位和診斷。
一、快速安裝
這里我們使用官網推薦的arthas-boot
下載arthas-boot.jar,然后用java -jar的方式啟動:
1 wget https://alibaba.github.io/arthas/arthas-boot.jar 2 java -jar arthas-boot.jar
打印幫助信息:
1 java -jar arthas-boot.jar -h
二、簡單使用
1.下載官方提供的Demo並啟動
arthas-demo是一個簡單的程序,每隔一秒生成一個隨機數,再執行質因式分解,並打印出分解結果。
1 wget https://alibaba.github.io/arthas/arthas-demo.jar 2 java -jar arthas-demo.jar
2.重新java -jar啟動下載好的arthas jar包
1 java -jar arthas-boot.jar
啟動完將看到:
1 ➜ githubTools java -jar arthas-boot.jar 2 [INFO] arthas-boot version: 3.1.0 3 [INFO] Found existing java process, please choose one and hit RETURN. 4 * [1]: 6419 arthas-demo.jar 5 [2]: 823
3.選擇應用java進程
Demo進程是第1個,則輸入1,再輸入回車/enter。Arthas會attach到目標進程上,並輸出日志:
1 [INFO] arthas home: /Users/zhangboqing/.arthas/lib/3.1.0/arthas 2 [INFO] Try to attach process 6419 3 [INFO] Attach process 6419 success. 4 [INFO] arthas-client connect 127.0.0.1 3658 5 ,---. ,------. ,--------.,--. ,--. ,---. ,---. 6 / O \ | .--. ''--. .--'| '--' | / O \ ' .-' 7 | .-. || '--'.' | | | .--. || .-. |`. `-. 8 | | | || |\ \ | | | | | || | | |.-' | 9 `--' `--'`--' '--' `--' `--' `--'`--' `--'`-----' 10 11 12 wiki: https://alibaba.github.io/arthas 13 version: 3.1.0 14 pid: 6419 15 time: 2019-02-16 23:22:11 16 17 $
這樣就進入到對應的進程中了,接下來的操作都是在當前進程中的
3.簡單命令介紹
1)dashboard
輸入dashboard,按回車/enter,會展示當前進程的信息,按ctrl+c可以中斷執行。
1 ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTED DAEMON 2 21 Timer-for-arthas-dashboard-b47de3b0-268b-4d6 system 10 RUNNABLE 76 0:0 false true 3 15 nioEventLoopGroup-3-1 system 10 RUNNABLE 23 0:0 false false 4 11 AsyncAppender-Worker-arthas-cache.result.Asy system 9 WAITING 0 0:0 false true 5 9 Attach Listener system 9 RUNNABLE 0 0:0 false true 6 3 Finalizer system 8 WAITING 0 0:0 false true 7 2 Reference Handler system 10 WAITING 0 0:0 false true 8 4 Signal Dispatcher system 9 RUNNABLE 0 0:0 false true 9 19 as-command-execute-daemon system 10 TIMED_WAITING 0 0:0 false true 10 13 job-timeout system 9 TIMED_WAITING 0 0:0 false true 11 1 main main 5 TIMED_WAITING 0 0:0 false false 12 14 nioEventLoopGroup-2-1 system 10 RUNNABLE 0 0:0 false false 13 18 nioEventLoopGroup-2-2 system 10 RUNNABLE 0 0:0 false false 14 16 pool-1-thread-1 system 5 TIMED_WAITING 0 0:0 false false 15 17 pool-2-thread-1 system 5 WAITING 0 0:0 false false 16 17 18 19 20 21 Memory used total max usage GC 22 heap 39M 123M 1820M 2.16% gc.ps_scavenge.count 3 23 ps_eden_space 28M 32M 672M 4.20% gc.ps_scavenge.time(ms) 32 24 ps_survivor_space 4M 5M 5M 99.38% gc.ps_marksweep.count 0 25 ps_old_gen 6M 85M 1365M 0.45% gc.ps_marksweep.time(ms) 0 26 nonheap 19M 20M -1 96.34% 27 code_cache 4M 4M 240M 1.78% 28 metaspace 13M 13M -1 96.48% 29 compressed_class_space 1M 1M 1024M 0.16% 30 direct 0K 0K - Infinity% 31 Runtime 32 os.name Mac OS X 33 os.version 10.14 34 java.version 1.8.0_112 35 java.home /Library/Java/JavaVirtualMachines/jdk1.8.0_1 36 12.jdk/Contents/Home/jre 37 systemload.average 1.69 38 processors 4
2)thread
通過thread命令來獲取到arthas-demo進程的Main Class
thread 1會打印線程ID 1的棧,通常是main函數的線程。
1 $ thread 1 2 "main" Id=1 TIMED_WAITING 3 at java.lang.Thread.sleep(Native Method) 4 at java.lang.Thread.sleep(Thread.java:340) 5 at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) 6 at demo.MathGame.main(MathGame.java:17) 7 8 Affect(row-cnt:0) cost in 17 ms.
3)jad
通過jad來反編譯Main Class
1 $ jad demo.MathGame 2 3 ClassLoader: 4 +-sun.misc.Launcher$AppClassLoader@6bc7c054 5 +-sun.misc.Launcher$ExtClassLoader@7e97d617 6 7 Location: 8 /Users/zhangboqing/Software/githubTools/arthas-demo.jar 9 10 /* 11 * Decompiled with CFR 0_132. 12 */ 13 package demo; 14 15 import java.io.PrintStream; 16 import java.util.ArrayList; 17 import java.util.Iterator; 18 import java.util.List; 19 import java.util.Random; 20 import java.util.concurrent.TimeUnit; 21 22 public class MathGame { 23 private static Random random = new Random(); 24 public int illegalArgumentCount = 0; 25 26 public static void main(String[] args) throws InterruptedException { 27 MathGame game = new MathGame(); 28 do { 29 game.run(); 30 TimeUnit.SECONDS.sleep(1L); 31 } while (true); 32 } 33 34 public void run() throws InterruptedException { 35 try { 36 int number = random.nextInt() / 10000; 37 List<Integer> primeFactors = this.primeFactors(number); 38 MathGame.print(number, primeFactors); 39 } 40 catch (Exception e) { 41 System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) + e.getMessage()); 42 } 43 } 44 45 public static void print(int number, List<Integer> primeFactors) { 46 StringBuffer sb = new StringBuffer("" + number + "="); 47 Iterator<Integer> iterator = primeFactors.iterator(); 48 while (iterator.hasNext()) { 49 int factor = iterator.next(); 50 sb.append(factor).append('*'); 51 } 52 if (sb.charAt(sb.length() - 1) == '*') { 53 sb.deleteCharAt(sb.length() - 1); 54 } 55 System.out.println(sb); 56 } 57 58 public List<Integer> primeFactors(int number) { 59 if (number < 2) { 60 ++this.illegalArgumentCount; 61 throw new IllegalArgumentException("number is: " + number + ", need >= 2"); 62 } 63 ArrayList<Integer> result = new ArrayList<Integer>(); 64 int i = 2; 65 while (i <= number) { 66 if (number % i == 0) { 67 result.add(i); 68 number /= i; 69 i = 2; 70 continue; 71 } 72 ++i; 73 } 74 return result; 75 } 76 } 77 78 Affect(row-cnt:1) cost in 830 ms. 79 $
4)watch
通過watch命令來查看demo.MathGame#primeFactors函數的返回值:
1 watch demo.MathGame primeFactors returnObj 2 Press Q or Ctrl+C to abort. 3 Affect(class-cnt:1 , method-cnt:1) cost in 59 ms. 4 ts=2019-02-16 23:31:38; [cost=1.710847ms] result=null 5 ts=2019-02-16 23:31:39; [cost=0.079354ms] result=null 6 ts=2019-02-16 23:31:40; [cost=0.251734ms] result=@ArrayList[ 7 @Integer[2], 8 @Integer[2], 9 @Integer[2], 10 @Integer[2], 11 @Integer[2], 12 @Integer[2], 13 @Integer[2], 14 @Integer[2], 15 @Integer[5], 16 @Integer[109], 17 ]
4.退出arthas
如果只是退出當前的連接,可以用quit或者exit命令。Attach到目標進程上的arthas還會繼續運行,端口會保持開放,下次連接時可以直接連接上。
如果想完全退出arthas,可以執行shutdown命令。
附錄:
Arthas 用戶文檔: https://alibaba.github.io/arthas/quick-start.html
Arthas github地址: https://github.com/alibaba/arthas