Arthas -問(wèn)題診斷神器

背景

出現(xiàn)線上故障,問(wèn)題在測(cè)試環(huán)境難以復(fù)現(xiàn)(非必現(xiàn)),開發(fā)人員人肉定位問(wèn)題慢幔睬,效率低下(無(wú)法跟測(cè)試環(huán)境一樣直接 debug)舱卡,短時(shí)間無(wú)法找出問(wèn)題根源要尔。影響比較緊急,業(yè)務(wù)方群里頻繁反饋(或許想拉幾個(gè)程序員祭天)。此時(shí),擁有屠龍之技傍身的你何愁無(wú)處施展嫉晶?大吼一聲放開那"妹子"讓我來(lái),一頓操作猛如虎。分分鐘就找到問(wèn)題。讓那群菜雞開發(fā)人員無(wú)地自容甚至跪下唱征服?


Arthas(阿爾薩斯) 能為你做什么馆截?
Alibaba開源的Java診斷工具翎蹈,深受開發(fā)者喜愛。當(dāng)你遇到以下類似問(wèn)題而束手無(wú)策時(shí),Arthas可以幫助你解決:

  • 這個(gè)類從哪個(gè) jar 包加載的肮塞?為什么會(huì)報(bào)各種類相關(guān)的 Exception?
  • 我改的代碼為什么沒有執(zhí)行到篮昧?難道是我沒 commit疚颊?分支搞錯(cuò)了油挥?
  • 遇到問(wèn)題無(wú)法在線上 debug惋鹅,難道只能通過(guò)加日志再重新發(fā)布嗎?
  • 線上遇到某個(gè)用戶的數(shù)據(jù)處理有問(wèn)題迟杂,但線上同樣無(wú)法 debug,線下無(wú)法重現(xiàn)泽谨!
  • 是否有一個(gè)全局視角來(lái)查看系統(tǒng)的運(yùn)行狀況?
  • 有什么辦法可以監(jiān)控到JVM的實(shí)時(shí)運(yùn)行狀態(tài)涂身?
  • 怎么快速定位應(yīng)用的熱點(diǎn)雄卷,生成火焰圖?
  • 怎樣直接從JVM內(nèi)查找某個(gè)類的實(shí)例蛤售?
    Arthas支持JDK 6+丁鹉,支持Linux/Mac/Windows妒潭,采用命令行交互模式,同時(shí)提供豐富的 Tab 自動(dòng)補(bǔ)全功能揣钦,進(jìn)一步方便進(jìn)行問(wèn)題的定位和診斷雳灾。

快速安裝

使用arthas-boot(推薦),下載arthas-boot.jar拂盯,然后用java -jar的方式啟動(dòng)佑女,啟動(dòng)之后按一次回車:

安裝:curl -O https://arthas.aliyun.com/arthas-boot.jar
啟動(dòng):java -jar arthas-boot.jar

[root@xxx-7b98666fbc-g79sx core]$ java -jar arthas-boot.jar 
[INFO] arthas-boot version: 3.1.5
[INFO] Found existing java process, please choose one and hit RETURN.
* [1]: 83 com.alibaba.dubbo.container.Main
  [2]: 158 qunar.tc.bistoury.indpendent.agent.Main

[INFO] arthas home: /root/.arthas/lib/3.5.5/arthas
[INFO] Try to attach process 83
[INFO] Attach process 83 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.                           
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'                          
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.                          
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |                         
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'                          

wiki       https://arthas.aliyun.com/doc                                        
tutorials  https://arthas.aliyun.com/doc/arthas-tutorials.html                  
version    3.5.5                                                                
main_class                                                                      
pid        83                                                                   
time       2022-01-20 23:18:45  

jvm相關(guān)

  • dashboard——當(dāng)前系統(tǒng)的實(shí)時(shí)數(shù)據(jù)面板
  • thread——查看當(dāng)前 JVM 的線程堆棧信息
  • getstatic——查看類的靜態(tài)屬性
dashboard
參數(shù)名稱 參數(shù)說(shuō)明
[i:] 刷新實(shí)時(shí)數(shù)據(jù)的時(shí)間間隔 (ms),默認(rèn)5000ms
[n:] 刷新實(shí)時(shí)數(shù)據(jù)的次數(shù)
數(shù)據(jù)說(shuō)明
  • ID: Java級(jí)別的線程ID谈竿,注意這個(gè)ID不能跟jstack中的nativeID一一對(duì)應(yīng)。
  • NAME: 線程名
  • GROUP: 線程組名
  • PRIORITY: 線程優(yōu)先級(jí), 1~10之間的數(shù)字摸吠,越大表示優(yōu)先級(jí)越高
  • STATE: 線程的狀態(tài)
  • CPU%: 線程的cpu使用率空凸。比如采樣間隔1000ms,某個(gè)線程的增量cpu時(shí)間為100ms寸痢,則cpu使用率=100/1000=10%
  • DELTA_TIME: 上次采樣之后線程運(yùn)行增量CPU時(shí)間呀洲,數(shù)據(jù)格式為秒
  • TIME: 線程運(yùn)行總CPU時(shí)間,數(shù)據(jù)格式為分:秒
  • INTERRUPTED: 線程當(dāng)前的中斷位狀態(tài)
  • DAEMON: 是否是daemon線程
$ dashboard -i 3000 -n 1
ID   NAME                           GROUP           PRIORITY   STATE     %CPU      DELTA_TIME TIME      INTERRUPTE DAEMON
-1   C2 CompilerThread0             -               -1         -         1.55      0.077      0:8.684   false      true
53   Timer-for-arthas-dashboard-07b system          5          RUNNABLE  0.08      0.004      0:0.004   false      true
22   scheduling-1                   main            5          TIMED_WAI 0.06      0.003      0:0.287   false      false
-1   C1 CompilerThread0             -               -1         -         0.06      0.003      0:2.171   false      true
-1   VM Periodic Task Thread        -               -1         -         0.03      0.001      0:0.092   false      true
49   arthas-NettyHttpTelnetBootstra system          5          RUNNABLE  0.02      0.001      0:0.156   false      true
16   Catalina-utility-1             main            1          TIMED_WAI 0.0       0.000      0:0.029   false      false
-1   G1 Young RemSet Sampling       -               -1         -         0.0       0.000      0:0.019   false      true
17   Catalina-utility-2             main            1          WAITING   0.0       0.000      0:0.025   false      false
34   http-nio-8080-ClientPoller     main            5          RUNNABLE  0.0       0.000      0:0.016   false      true
23   http-nio-8080-BlockPoller      main            5          RUNNABLE  0.0       0.000      0:0.011   false      true
-1   VM Thread                      -               -1         -         0.0       0.000      0:0.032   false      true
-1   Service Thread                 -               -1         -         0.0       0.000      0:0.006   false      true
-1   GC Thread#5                    -               -1         -         0.0       0.000      0:0.043   false      true
Memory                     used     total    max      usage    GC
heap                       36M      70M      4096M    0.90%    gc.g1_young_generation.count   12
g1_eden_space              6M       18M      -1       33.33%                                  86
g1_old_gen                 30M      50M      4096M    0.74%    gc.g1_old_generation.count     0
g1_survivor_space          491K     2048K    -1       24.01%   gc.g1_old_generation.time(ms)  0
nonheap                    66M      69M      -1       96.56%
codeheap_'non-nmethods'    1M       2M       5M       22.39%
metaspace                  46M      47M      -1       98.01%
Runtime
os.name                                                        Mac OS X
os.version                                                     10.15.4
java.version                                                   15
java.home                                                      /Library/Java/JavaVirtualMachines/jdk-15.jdk/Contents/Home
systemload.average                                             10.68
processors                                                     8
uptime                                                         272s
thread
參數(shù)名稱 參數(shù)說(shuō)明
id 線程id
[n:] 指定最忙的前N個(gè)線程并打印堆棧
[b] 找出當(dāng)前阻塞其他線程的線程
[i <value>] 指定cpu使用率統(tǒng)計(jì)的采樣間隔啼止,單位為毫秒道逗,默認(rèn)值為200
[--all] 顯示所有匹配的線程
[-- state] 線上指定狀態(tài)匹配的線程
[arthas@84]$ thread
Threads Total: 261, NEW: 0, RUNNABLE: 36, BLOCKED: 0, WAITING: 131, TIMED_WAITING: 85, TERMINATED: 0, Internal threads: 9                                                            
ID      NAME                                         GROUP                 PRIORITY        STATE          %CPU           DELTA_TIME     TIME           INTERRUPTED    DAEMON         
331     arthas-command-execute                       system                5               RUNNABLE       1.91           0.003          0:0.009        false          true           
-1      C1 CompilerThread1                           -                     -1              -              1.03           0.002          0:4.244        false          true           
24      com.alibaba.nacos.client.Worker.fixed-172.30 main                  5               TIMED_WAITING  0.23           0.000          0:0.826        false          true           
73      I-scheduler-1                                main                  5               TIMED_WAITING  0.21           0.000          0:0.564        false          false          
59      erlang-apm-reporter-0                        main                  5               TIMED_WAITING  0.17           0.000          0:0.496        false          true           
-1      C2 CompilerThread0                           -                     -1              -              0.08           0.000          0:22.251       false          true           
6       org.jacoco.agent.rt.internal_43f5073.output. main                  5               RUNNABLE       0.08           0.000          0:0.073        false          true           
277     DubboResponseTimeoutScanTimer                main                  5               TIMED_WAITING  0.07           0.000          0:0.068        false          true           
-1      VM Periodic Task Thread                      -                     -1              -              0.04           0.000          0:0.108        false          true           
38      server-timer                                 main                  5               TIMED_WAITING  0.02           0.000          0:0.002        false          true           
58      Abandoned connection cleanup thread          main                  5               TIMED_WAITING  0.02           0.000          0:0.048        false          true           
145     ClientHouseKeepingService                    main                  5               TIMED_WAITING  0.02           0.000          0:0.009        false          true           
83      ClientHouseKeepingService                    main                  5               TIMED_WAITING  0.01           0.000          0:0.007        false          true           
136     New I/O client worker #1-2                   main                  5               RUNNABLE       0.01           0.000          0:0.016        false          true           

當(dāng)沒有參數(shù)時(shí),顯示第一頁(yè)線程的信息献烦,默認(rèn)按照CPU增量時(shí)間降序排列滓窍,只顯示第一頁(yè)數(shù)據(jù)。

thread ID :列出指定ID線程巩那,可以從 dashboard 中獲取ID

[arthas@84]$ thread 24
"com.alibaba.nacos.client.Worker.fixed-172.30.4.32_8848-18617388-78f3-4a99-aab2-7462d8b6973e" Id=24 TIMED_WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@137b06de
    at sun.misc.Unsafe.park(Native Method)
    -  waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@137b06de
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

thread -n 3 -i 1000 :列出1000ms內(nèi)最忙的3個(gè)線程棧

$ thread -n 3 -i 1000
"as-command-execute-daemon" Id=4759 cpuUsage=23% RUNNABLE
    at sun.management.ThreadImpl.dumpThreads0(Native Method)
    at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:133)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:79)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:96)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:27)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:125)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:122)
    at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:332)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:756)
 
    Number of locked synchronizers = 1
    - java.util.concurrent.ThreadPoolExecutor$Worker@546aeec1

thread --state waiting : 匹配指定狀態(tài)的線程

[arthas@28114]$ thread --state WAITING
Threads Total: 16, NEW: 0, RUNNABLE: 9, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0
ID   NAME                           GROUP           PRIORITY   STATE     %CPU      DELTA_TIME TIME      INTERRUPTE DAEMON
3    Finalizer                      system          8          WAITING   0.0       0.000      0:0.000   false      true
20   arthas-UserStat                system          9          WAITING   0.0       0.000      0:0.001   false      true
14   arthas-timer                   system          9          WAITING   0.0       0.000      0:0.000   false      true

jad

jad--反編譯指定已加載類的源碼吏夯,將 JVM 中實(shí)際運(yùn)行的 class 的 byte code 反編譯成 java 代碼,便于你理解業(yè)務(wù)邏輯
tips: 當(dāng)不確定線上的代碼是否部署的最新時(shí)可以查看代碼對(duì)比即可

參數(shù)名稱 參數(shù)說(shuō)明
class-pattern 類名表達(dá)式匹配
[c:] 類所屬 ClassLoader 的 hashcode
[classLoaderClass:] 指定執(zhí)行表達(dá)式的 ClassLoader 的 class name
[E] 開啟正則表達(dá)式匹配即横,默認(rèn)為通配符匹配

反編譯時(shí)只顯示源代碼

[arthas@93]$ jad --source-only org.example.entity.Record
       /*
        * Decompiled with CFR.
        */
    package org.example.entity;

    import org.example.enums.CaseType;


    public class Record {
        private final String commitId;
        /**
         * 應(yīng)用名稱
         */
        private final String appName;
        /**
         * 分支
         */
        private final String branch;
        /**
         * 運(yùn)行環(huán)境
         */
        private final String env;
        /**
         * 錄制標(biāo)題
         */
        private final String recordTitle;
        /**
         * 錄制任務(wù)類型:如:接口自動(dòng)化測(cè)試
         */
        private final CaseType recordType;

        public String getCommitId() { return commitId; }

        public String getAppName() {
            return appName;
        }

......

反編譯指定的函數(shù)

[arthas@93]$ jad  org.example.entity.Record getCommitId
...省略...  跟上面類似噪生,只是展示對(duì)應(yīng)的函數(shù)代碼

反編譯時(shí)指定ClassLoader

$ jad org.apache.log4j.Logger
 
Found more than one class for: org.apache.log4j.Logger, Please use jad -c hashcode org.apache.log4j.Logger
HASHCODE  CLASSLOADER
69dcaba4  +-monitor's ModuleClassLoader
6e51ad67  +-java.net.URLClassLoader@6e51ad67
            +-sun.misc.Launcher$AppClassLoader@6951a712
            +-sun.misc.Launcher$ExtClassLoader@6fafc4c2
2bdd9114  +-pandora-qos-service's ModuleClassLoader
4c0df5f8  +-pandora-framework's ModuleClassLoader
 
Affect(row-cnt:0) cost in 38 ms.
$ jad org.apache.log4j.Logger -c 69dcaba4
 
ClassLoader:
+-monitor's ModuleClassLoader
 
Location:
/Users/admin/app/log4j-1.2.14.jar
 
package org.apache.log4j;
 
import org.apache.log4j.spi.*;
 
public class Logger extends Category
{
    private static final String FQCN;
 
    protected Logger(String name)
    {
        super(name);
    }
 
...
 
Affect(row-cnt:1) cost in 190 ms.

當(dāng)有多個(gè) ClassLoader 都加載了這個(gè)類時(shí),jad 命令會(huì)輸出對(duì)應(yīng) ClassLoader 實(shí)例的 hashcode东囚,然后你只需要重新執(zhí)行 jad 命令跺嗽,并使用參數(shù) -c <hashcode> 就可以反編譯指定 ClassLoader 加載的那個(gè)類了。

watch(推薦)

命令 watch——方法執(zhí)行數(shù)據(jù)觀測(cè)页藻,都通過(guò)字節(jié)碼增強(qiáng)技術(shù)來(lái)實(shí)現(xiàn)的桨嫁,會(huì)在指定類的方法中插入一些切面來(lái)實(shí)現(xiàn)數(shù)據(jù)統(tǒng)計(jì)和觀測(cè),因此在線上惕橙、預(yù)發(fā)使用時(shí)瞧甩,請(qǐng)盡量明確需要觀測(cè)的類、方法以及條件弥鹦,診斷結(jié)束要執(zhí)行 stop 命令肚逸。

watch 讓你能方便的觀察到指定函數(shù)的調(diào)用情況(推薦)爷辙。能觀察到的范圍為:返回值、拋出異常朦促、入?yún)ⅰ?/p>

參數(shù)說(shuō)明
參數(shù)名稱 參數(shù)說(shuō)明
class-pattern 類名表達(dá)式匹配
method-pattern 函數(shù)名表達(dá)式匹配
express 觀察表達(dá)式膝晾,默認(rèn)值:{params, target, returnObj}
condition-express 條件表達(dá)式
[b] 在函數(shù)調(diào)用之前觀察
[e] 在函數(shù)異常之后觀察
[s] 在函數(shù)返回之后觀察
[f] 在函數(shù)結(jié)束之后(正常返回和異常返回)觀察
[E] 開啟正則表達(dá)式匹配,默認(rèn)為通配符匹配
[x:] 指定輸出結(jié)果的屬性遍歷深度务冕,默認(rèn)為 1

特別說(shuō)明:

  • watch 命令定義了4個(gè)觀察事件點(diǎn)血当,即(-b 函數(shù)調(diào)用前 -e 函數(shù)異常后 -s 函數(shù)返回后 -f 函數(shù)結(jié)束后)
  • 4個(gè)觀察事件點(diǎn) -f 默認(rèn)打開,當(dāng)指定觀察點(diǎn)被打開后禀忆,在相應(yīng)事件點(diǎn)會(huì)對(duì)觀察表達(dá)式進(jìn)行求值并輸出
  • 這里要注意函數(shù)入?yún)⒑统鰠⒌膮^(qū)別臊旭,有可能在中間被修改導(dǎo)致前后不一致,除了 -b 事件點(diǎn) params 代表函數(shù)入?yún)⑼饴嵬耍溆嗍录即砗瘮?shù)出參
  • 當(dāng)使用 -b 時(shí)离熏,由于觀察事件點(diǎn)是在函數(shù)調(diào)用前,此時(shí)返回值或異常均不存在
  • 在watch命令的結(jié)果里戴涝,會(huì)打印出 location 信息滋戳。location有三種可能值:AtEnter,AtExit啥刻,AtExceptionExit奸鸯。對(duì)應(yīng)函數(shù)入口,函數(shù)正常 return可帽,函數(shù)拋出異常
示例

觀察函數(shù)調(diào)用返回時(shí)的參數(shù)娄涩、this對(duì)象、返回值
注意:express 觀察表達(dá)式蘑拯,默認(rèn)值是{params, target, returnObj}

[arthas@93]$ watch org.demo.compensate.CompensateWriteImpl create -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 286 ms, listenerId: 3
method=org.demo.compensate.CompensateWriteImpl.create location=AtExit
ts=2022-01-20 09:40:20; [cost=264.505867ms] result=@ArrayList[
    @Object[][
        @CompensateCreateAo[CompensateCreateAo(uniqueId=657cc750cae44a8aac288d5a4ace7f01, compensateMode=0, compensateAccountType=0, moneyValidTime=400, priceMoney=53.01, compensateMoney=12.0, compensateTotalMoney=12.0, compensateDesc=12, compensateImages=[http://image.baidu.com/7dfa4d7b8eb3e5184fa32736395d689d.png], batchNo=null)],
    ],
    @CompensateWriteProviderImpl[
        log=@Log4jLoggerAdapter[org.slf4j.impl.Log4jLoggerAdapter(org.demo.compensate.CompensateWriteImpl)],
        domainService=@CompensateDomainService[org.demo.tian.compensates.domain.CompensateDomainService@20b614e],
        orderServiceProxy=@OrderServiceProxy[org.demo.tian.compensates.infrastructure.service.proxy.OrderServiceProxy@56089ff9],
        compensatePriceQueryService=@CompensatePriceQueryService[org.demo.tian.compensates.infrastructure.service.CompensatePriceQueryService@4de2237],
        returnCompensateServiceProxy=@ReturnCompensateServiceProxy[org.demo.tian.compensates.infrastructure.service.proxy.ReturnCompensateServiceProxy@61f93b2],
        compensateQueryRepository=@CompensateQueryRepository[org.demo.tian.compensates.infrastructure.repository.CompensateQueryRepository@743a90e1],
        compensateQueryService=@CompensateQueryService[org.demo.tian.compensates.infrastructure.service.CompensateQueryService@25960369],
        compensateDao=@$Proxy74[org.apache.ibatis.binding.MapperProxy@7ad8f798],
        afterImageSignService=@AfterImageSignServiceImpl[org.demo.butian.service.sameimage.AfterImageSignServiceImpl@4ef0e837],
        orderReturnService=@proxy7[com.alibaba.dubbo.common.bytecode.proxy7@6d8dabbe],
        xyUserLabelProvider=@XyUserLabelProviderImpl[org.demo.butian.provider.XyUserLabelProviderImpl@78206104],
        orderReturnServiceProxy=@OrderReturnServiceProxy[org.demo.tian.compensates.infrastructure.service.proxy.OrderReturnServiceProxy@fdb1e5c],
        userServiceProxy=@UserServiceProxy[org.demo.tian.compensates.infrastructure.service.proxy.UserServiceProxy@515d351e],
        logisticsCostRuleProvider=@proxy37[com.alibaba.dubbo.common.bytecode.proxy37@a0da1e0],
        redisCache=@ClusterRedis[org.demo.base.redis.ClusterRedis@249864b0],
        priceAlreadyApplyService=@[org.demo.compensate.CompensateWriteImpl$1@2ec15f95],
        freightAlreadyApplyService=@[org.demo.compensate.CompensateWriteImpl$2@139819e2],
        checkPriceService=@ArrayList[isEmpty=false;size=1],
        checkFreightService=@ArrayList[isEmpty=false;size=1],
        $jacocoData=@boolean[][isEmpty=false;size=388],
    ],
    @CompensateRo[
        extend=null,
        $jacocoData=@boolean[][isEmpty=false;size=25],
        code=@Integer[0],
        msg=@String[],
        data=@CompensateDataRo[CompensateRo.CompensateDataRo(compensateNo=543873894546260715)],
        DEFAULT_SUCCESS_MESSAGE=@String[請(qǐng)求成功],
        $jacocoData=@boolean[][isEmpty=false;size=41],
    ],
]

分析一下上面的結(jié)果:

  • 可以看出result是一個(gè)ArrayList[Object [][@CompensateCreateAo, @CompensateWriteProviderImpl, @CompensateRo]]钝满,分別對(duì)應(yīng)express {params, target, returnObj}
  • 上面的示例中 location=AtExit 表示結(jié)果正確返回了,如果location=AtExceptionExit申窘,說(shuō)明函數(shù)拋出異常了弯蚜,returnObj可能就是null。
  • 輸入的條件表達(dá)式 - x 2 表示遍歷的深度為3剃法,默認(rèn)是1.

觀察函數(shù)調(diào)用入口的參數(shù)和返回值

  • -b 表示在事件之前觀察函數(shù)入?yún)⑿畔⑺檗啵@時(shí)不關(guān)注結(jié)果返回。
[arthas@93]$ watch org.demo.compensate.CompensateWriteImpl create -x 2 -b
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 319 ms, listenerId: 4
method=org.demo.compensate.CompensateWriteImpl.create location=AtEnter
ts=2022-01-20 09:53:50; [cost=0.019688ms] result=@ArrayList[
    @Object[][
        @CompensateCreateAo[CompensateCreateAo(uniqueId=e299d35616bc4ae3b914b65601807939, compensateMode=0, compensateAccountType=0, moneyValidTime=400, priceMoney=53.01, compensateMoney=13.0, compensateTotalMoney=13.0, compensateDesc=13, compensateImages=[http://image.baidu.com/7dfa4d7b8eb3e5184fa32736395d689d.png], batchNo=null)],
    ],
    @CompensateWriteProviderImpl[
        log=@Log4jLoggerAdapter[org.slf4j.impl.Log4jLoggerAdapter(org.demo.compensate.CompensateWriteImpl)],
        domainService=@CompensateDomainService[org.demo.butian.compensate.domain.CompensateDomainService@20b614e],
        orderServiceProxy=@OrderServiceProxy[org.demo.butian.compensate.infrastructure.service.proxy.OrderServiceProxy@56089ff9],
        compensatePriceQueryService=@CompensatePriceQueryService[org.demo.butian.compensate.infrastructure.service.CompensatePriceQueryService@4de2237],
        returnCompensateServiceProxy=@ReturnCompensateServiceProxy[org.demo.butian.compensate.infrastructure.service.proxy.ReturnCompensateServiceProxy@61f93b2],
        compensateQueryRepository=@CompensateQueryRepository[org.demo.butian.compensate.infrastructure.repository.CompensateQueryRepository@743a90e1],
        compensateQueryService=@CompensateQueryService[org.demo.butian.compensate.infrastructure.service.CompensateQueryService@25960369],
        compensateDao=@$Proxy74[org.apache.ibatis.binding.MapperProxy@7ad8f798],
        afterImageSignService=@AfterImageSignServiceImpl[org.demo.butian.service.sameimage.AfterImageSignServiceImpl@4ef0e837],
        orderReturnService=@proxy7[com.alibaba.dubbo.common.bytecode.proxy7@6d8dabbe],
        xyUserLabelProvider=@XyUserLabelProviderImpl[org.demo.butian.provider.XyUserLabelProviderImpl@78206104],
        orderReturnServiceProxy=@OrderReturnServiceProxy[org.demo.butian.compensate.infrastructure.service.proxy.OrderReturnServiceProxy@fdb1e5c],
        userServiceProxy=@UserServiceProxy[org.demo.butian.compensate.infrastructure.service.proxy.UserServiceProxy@515d351e],
        logisticsCostRuleProvider=@proxy37[com.alibaba.dubbo.common.bytecode.proxy37@a0da1e0],
        redisCache=@ClusterRedis[org.demo.base.redis.ClusterRedis@249864b0],
        priceAlreadyApplyService=@[org.demo.compensate.CompensateWriteImpl$1@2ec15f95],
        freightAlreadyApplyService=@[org.demo.compensate.CompensateWriteImpl$2@139819e2],
        checkPriceService=@ArrayList[isEmpty=false;size=1],
        checkFreightService=@ArrayList[isEmpty=false;size=1],
        $jacocoData=@boolean[][isEmpty=false;size=388],
    ],
    null,
]

對(duì)比上一個(gè)示例贷洲,此時(shí)返回值為空(事件點(diǎn)為函數(shù)執(zhí)行前收厨,因此獲取不到返回值)

同時(shí)觀察函數(shù)調(diào)用前和函數(shù)返回后

  • 當(dāng)想觀察函數(shù)調(diào)用前合結(jié)果返回之間的差異時(shí)
[arthas@93]$ watch org.demo.compensate.CompensateWriteImpl create -x 2 -b -s -n 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 281 ms, listenerId: 5
method=org.demo.compensate.CompensateWriteImpl.create location=AtEnter
ts=2022-01-20 09:58:12; [cost=0.015069ms] result=@ArrayList[
    @Object[][
        @CompensateCreateAo[CompensateCreateAo(uniqueId=04c849a33e324ba09ce1d8f95bb1b0db, compensateMode=0, compensateAccountType=0, moneyValidTime=400, priceMoney=53.01, compensateMoney=14.0, compensateTotalMoney=14.0, compensateDesc=14, compensateImages=[http://image.baidu.com/7dfa4d7b8eb3e5184fa32736395d689d.png], batchNo=null)],
    ],
    @CompensateWriteProviderImpl[
        log=@Log4jLoggerAdapter[org.slf4j.impl.Log4jLoggerAdapter(org.demo.compensate.CompensateWriteImpl)],
        domainService=@CompensateDomainService[org.demo.compensate.domain.CompensateDomainService@20b614e],
        orderServiceProxy=@OrderServiceProxy[org.demo.compensate.infrastructure.service.proxy.OrderServiceProxy@56089ff9],
        compensatePriceQueryService=@CompensatePriceQueryService[org.demo.compensate.infrastructure.service.CompensatePriceQueryService@4de2237],
        returnCompensateServiceProxy=@ReturnCompensateServiceProxy[org.demo.compensate.infrastructure.service.proxy.ReturnCompensateServiceProxy@61f93b2],
        compensateQueryRepository=@CompensateQueryRepository[org.demo.compensate.infrastructure.repository.CompensateQueryRepository@743a90e1],
        compensateQueryService=@CompensateQueryService[org.demo.compensate.infrastructure.service.CompensateQueryService@25960369],
        compensateDao=@$Proxy74[org.apache.ibatis.binding.MapperProxy@7ad8f798],
        afterImageSignService=@AfterImageSignServiceImpl[org.demo.service.sameimage.AfterImageSignServiceImpl@4ef0e837],
        orderReturnService=@proxy7[com.alibaba.dubbo.common.bytecode.proxy7@6d8dabbe],
        xyUserLabelProvider=@XyUserLabelProviderImpl[org.demo.provider.XyUserLabelProviderImpl@78206104],
        orderReturnServiceProxy=@OrderReturnServiceProxy[org.demo.compensate.infrastructure.service.proxy.OrderReturnServiceProxy@fdb1e5c],
        userServiceProxy=@UserServiceProxy[org.demo.compensate.infrastructure.service.proxy.UserServiceProxy@515d351e],
        logisticsCostRuleProvider=@proxy37[com.alibaba.dubbo.common.bytecode.proxy37@a0da1e0],
        redisCache=@ClusterRedis[com.yunji.base.redis.ClusterRedis@249864b0],
        priceAlreadyApplyService=@[org.demo.compensate.CompensateWriteImpl$1@2ec15f95],
        freightAlreadyApplyService=@[org.demo.compensate.CompensateWriteImpl$2@139819e2],
        checkPriceService=@ArrayList[isEmpty=false;size=1],
        checkFreightService=@ArrayList[isEmpty=false;size=1],
        $jacocoData=@boolean[][isEmpty=false;size=388],
    ],
    null,
]
method=org.demo.compensate.CompensateWriteImpl.create location=AtExit
ts=2022-01-20 09:58:12; [cost=4.71715649799504E10ms] result=@ArrayList[
    @Object[][
        @CompensateCreateAo[CompensateCreateAo(uniqueId=04c849a33e324ba09ce1d8f95bb1b0db, compensateMode=0, compensateAccountType=0, moneyValidTime=400, priceMoney=53.01, compensateMoney=14.0, compensateTotalMoney=14.0, compensateDesc=14, compensateImages=[http://image.baidu.com/7dfa4d7b8eb3e5184fa32736395d689d.png], batchNo=null)],
    ],
    @CompensateWriteProviderImpl[
        log=@Log4jLoggerAdapter[org.slf4j.impl.Log4jLoggerAdapter(org.demo.compensate.CompensateWriteImpl)],
        domainService=@CompensateDomainService[org.demo.compensate.domain.CompensateDomainService@20b614e],
        orderServiceProxy=@OrderServiceProxy[org.demo.compensate.infrastructure.service.proxy.OrderServiceProxy@56089ff9],
        compensatePriceQueryService=@CompensatePriceQueryService[org.demo.compensate.infrastructure.service.CompensatePriceQueryService@4de2237],
        returnCompensateServiceProxy=@ReturnCompensateServiceProxy[org.demo.compensate.infrastructure.service.proxy.ReturnCompensateServiceProxy@61f93b2],
        compensateQueryRepository=@CompensateQueryRepository[org.demo.compensate.infrastructure.repository.CompensateQueryRepository@743a90e1],
        compensateQueryService=@CompensateQueryService[org.demo.compensate.infrastructure.service.CompensateQueryService@25960369],
        compensateDao=@$Proxy74[org.apache.ibatis.binding.MapperProxy@7ad8f798],
        afterImageSignService=@AfterImageSignServiceImpl[org.demo.service.sameimage.AfterImageSignServiceImpl@4ef0e837],
        orderReturnService=@proxy7[com.alibaba.dubbo.common.bytecode.proxy7@6d8dabbe],
        xyUserLabelProvider=@XyUserLabelProviderImpl[org.demo.provider.XyUserLabelProviderImpl@78206104],
        orderReturnServiceProxy=@OrderReturnServiceProxy[org.demo.compensate.infrastructure.service.proxy.OrderReturnServiceProxy@fdb1e5c],
        userServiceProxy=@UserServiceProxy[org.demo.compensate.infrastructure.service.proxy.UserServiceProxy@515d351e],
        logisticsCostRuleProvider=@proxy37[com.alibaba.dubbo.common.bytecode.proxy37@a0da1e0],
        redisCache=@ClusterRedis[com.yunji.base.redis.ClusterRedis@249864b0],
        priceAlreadyApplyService=@[org.demo.compensate.CompensateWriteImpl$1@2ec15f95],
        freightAlreadyApplyService=@[org.demo.compensate.CompensateWriteImpl$2@139819e2],
        checkPriceService=@ArrayList[isEmpty=false;size=1],
        checkFreightService=@ArrayList[isEmpty=false;size=1],
        $jacocoData=@boolean[][isEmpty=false;size=388],
    ],
    @CompensateRo[
        extend=null,
        $jacocoData=@boolean[][isEmpty=false;size=25],
        code=@Integer[0],
        msg=@String[],
        data=@CompensateDataRo[CompensateRo.CompensateDataRo(compensateNo=543882889013084907)],
        DEFAULT_SUCCESS_MESSAGE=@String[請(qǐng)求成功],
        $jacocoData=@boolean[][isEmpty=false;size=41],
    ],
]
Command execution times exceed limit: 2, so command will exit. You can set it with -n option.
  • 參數(shù)里 -n 2,表示只執(zhí)行兩次,從上面的結(jié)果觀察發(fā)現(xiàn)有兩次結(jié)果記錄
  • 這里輸出結(jié)果中优构,第一次輸出的是函數(shù)調(diào)用前的觀察表達(dá)式的結(jié)果诵叁,第二次輸出的是函數(shù)返回后的表達(dá)式的結(jié)果
  • 結(jié)果的輸出順序和事件發(fā)生的先后順序一致,和命令中 -s -b 的順序無(wú)關(guān)

條件表達(dá)式的例子

  • 查詢?nèi)雲(yún)M足某個(gè)指定條件時(shí)
[arthas@93]$ watch org.demo.compensate.CompensateWriteImpl create  "params[0]=0"
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 268 ms, listenerId: 6
method=org.demo.compensate.CompensateWriteImpl.create location=AtExit
ts=2022-01-20 10:15:38; [cost=250.521304ms] result=@Integer[0]
  • 只有滿足條件params [1] = 0 的調(diào)用钦椭,才會(huì)有響應(yīng)拧额。

觀察異常信息的例子
當(dāng)想觀察某個(gè)指定異常信息時(shí)

$ watch demo.MathGame primeFactors "{params[0],throwExp}" -e -x 2
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 62 ms.
ts=2018-12-03 19:38:00; [cost=1.414993ms] result=@ArrayList[
    @Integer[-1120397038],
    java.lang.IllegalArgumentException: number is: -1120397038, need >= 2
    at demo.MathGame.primeFactors(MathGame.java:46)
    at demo.MathGame.run(MathGame.java:24)
    at demo.MathGame.main(MathGame.java:16)
,
]
  • -e表示拋出異常時(shí)才觸發(fā)
  • express中碑诉,表示異常信息的變量是throwExp

watch 還有很多其他的操作......

vmtool

vmtool get instances invoke method field(推薦)
利用JVMTI接口,實(shí)現(xiàn)查詢內(nèi)存對(duì)象侥锦,強(qiáng)制GC等功能(這個(gè)是直接去觸發(fā)調(diào)用對(duì)象进栽,而watch是觀察對(duì)象。一個(gè)是主動(dòng)一個(gè)是被動(dòng)), 在線上定位某個(gè)對(duì)象調(diào)用的時(shí)候非常方便恭垦。

[arthas@84]$ vmtool --action getInstances --className org.demo.CompensateProviderImpl  --express 'instances[0].getCompensateAppealInfo(13092L,"code")' --limit 5
@CompensateAppealVo[
    appealId=@Long[13092],
    appealNo=@Long[0],
    orderId=@String[DMPP71188700778259296256],
    storeCode=@String[DM089545],
    compensateId=@Long[543193216242532498],
    compensateTime=@Date[2022-01-19 11:07:57,000],
    status=@Integer[20],
    appealApplyTime=@Date[2022-01-19 11:09:18,000],
    appealClosedTime=null,
    appealSuccessTime=null,
    appealAmount=@BigDecimal[5.00],
    yunjiAmount=@BigDecimal[0.00],
    providerAmount=@BigDecimal[5.00],
    customServiceAmount=@BigDecimal[0.00],
    logisticsAmount=@BigDecimal[0.00],
    warehouseAmount=@BigDecimal[0.00],
    rejectCount=@Integer[0],
    reasonOne=@Integer[1],
    reasonTwo=@Integer[0],
    imgSet=@String[http://image.baidu.com/1d86f25e74d005149c6722707e26c2ab.png],
    remark=@String[1],
    statusValue=@String[已申訴待仲裁],
    storeName=@String[虐心賣場(chǎng)型旗艦店],
    reasonOneName=@String[金額不認(rèn)可],
    reasonTwoName=null,
    countdownTime=@Long[0],
    compensateTotalMoney=@Double[5.0],
    appealBearSide=@String[],
    barcode=@String[POPB19137206],
    itemName=@String[小米12Pro],
    itemModel=@String[N銷售屬性-內(nèi)存:16  N銷售屬性-機(jī)型顏色:白色],
    buyCount=@Integer[0],
    itemId=@Integer[1008688],
    itemPrice=@Double[30.9],
    itemImg=@String[http://image.baidu.com/admin_dev/e6efe753d2e137c95190102fda069181.jpg?imageView2/1/w/300/h/300],
    popAppealPrivilege=@Boolean[false],
    videoUrl=@String[http://static.baidu.com/admin_dev/1ef44932788743d35622d392074c83ad.mp4],
    videoImg=@String[http://static.baidu.com/admin_dev/1ef44932788743d35622d392074c83ad.mp4?vframe/jpg/offset/1],
    businessType=@String[CP],
    businessTypeStr=@String[補(bǔ)償],
    businessId=@String[543193216242532498],
    $jacocoData=@boolean[][isEmpty=false;size=83],
]
  • 通過(guò) --limit 參數(shù)快毛,可以限制返回值數(shù)量,避免獲取超大數(shù)據(jù)時(shí)對(duì)JVM造成壓力番挺。默認(rèn)值是10唠帝。
    以上就是平時(shí)比較常用到的一些命令用法,當(dāng)然 arthas 還有很多強(qiáng)大的功能玄柏,有興趣的可以去查閱:
    Arthas Documentation

插件 arthas

以上你可能覺得命令太多没隘,記不住。沒關(guān)系禁荸,idea已經(jīng)集成了插件,可以一鍵生成命令表達(dá)式阀湿。soEasy!
Jetbrains 插件獲取地址: https://plugins.jetbrains.com/plugin/11386-alibaba-cloud-toolkit
或者直接在idea安裝插件plus



最后

合理的使用工具能夠提升工作效率赶熟,但是不能過(guò)度依賴工具,應(yīng)該關(guān)注問(wèn)題背后涉及的底層邏輯陷嘴。
此篇內(nèi)容僅適用于對(duì)Java有一定基礎(chǔ)的同學(xué) ......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末映砖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子灾挨,更是在濱河造成了極大的恐慌邑退,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劳澄,死亡現(xiàn)場(chǎng)離奇詭異地技,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)秒拔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門莫矗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人砂缩,你說(shuō)我怎么就攤上這事作谚。” “怎么了庵芭?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵妹懒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我双吆,道長(zhǎng)眨唬,這世上最難降的妖魔是什么会前? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮单绑,結(jié)果婚禮上回官,老公的妹妹穿的比我還像新娘。我一直安慰自己搂橙,他們只是感情好歉提,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著区转,像睡著了一般苔巨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上废离,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天侄泽,我揣著相機(jī)與錄音,去河邊找鬼蜻韭。 笑死悼尾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肖方。 我是一名探鬼主播闺魏,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼俯画!你這毒婦竟也來(lái)了析桥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤艰垂,失蹤者是張志新(化名)和其女友劉穎泡仗,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猜憎,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娩怎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拉宗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峦树。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖旦事,靈堂內(nèi)的尸體忽然破棺而出魁巩,到底是詐尸還是另有隱情,我是刑警寧澤姐浮,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布谷遂,位于F島的核電站,受9級(jí)特大地震影響卖鲤,放射性物質(zhì)發(fā)生泄漏肾扰。R本人自食惡果不足惜畴嘶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望集晚。 院中可真熱鬧窗悯,春花似錦、人聲如沸偷拔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)莲绰。三九已至欺旧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛤签,已是汗流浹背辞友。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留震肮,地道東北人称龙。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像戳晌,于是被迫代替她去往敵國(guó)和親茵瀑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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