背景
出現(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é) ......