目录
1.安装使用
2.attach一个进程
3.常用命令的接触 dashboard thread
4.jvm相关命令:sysprop sysenv vmoption getstatic ognl
5.class 和classloader相关的命令
阿尔萨斯的使用案例
用户文档:Arthas 用户文档 — Arthas 3.5.6 文档
当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
-
这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
-
我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
-
遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
-
线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
-
是否有一个全局视角来查看系统的运行状况?
-
有什么办法可以监控到JVM的实时运行状态?
-
怎么快速定位应用的热点,生成火焰图?
-
怎样直接从JVM内查找某个类的实例?
1.安装使用
前提:Arthas是一个java的程序,运行前需保证机器上有正在运行的java进程,不然Arthas无法启动
下载Arthas :curl -O https://arthas.aliyun.com/arthas-boot.jar
2.启动arthas
在命令行下面执行(使用和目标进程一致的用户启动,否则可能 attach 失败):
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
- 执行该程序的用户需要和目标进程具有相同的权限。比如以
admin
用户来执行:sudo su admin && java -jar arthas-boot.jar
或sudo -u admin -EH java -jar arthas-boot.jar
。 - 如果 attach 不上目标进程,可以查看
~/logs/arthas/
目录下的日志。 - 如果下载速度比较慢,可以使用 aliyun 的镜像:
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
java -jar arthas-boot.jar -h
打印更多参数信息。
3. 查看 dashboard
输入dashboard,按回车/enter
,会展示当前进程的信息,按ctrl+c
可以中断执行。
$ dashboard
ID NAME GROUP PRIORI STATE %CPU TIME INTERRU DAEMON
17 pool-2-thread-1 system 5 WAITIN 67 0:0 false false
27 Timer-for-arthas-dashb system 10 RUNNAB 32 0:0 false true
11 AsyncAppender-Worker-a system 9 WAITIN 0 0:0 false true
9 Attach Listener system 9 RUNNAB 0 0:0 false true
3 Finalizer system 8 WAITIN 0 0:0 false true
2 Reference Handler system 10 WAITIN 0 0:0 false true
4 Signal Dispatcher system 9 RUNNAB 0 0:0 false true
26 as-command-execute-dae system 10 TIMED_ 0 0:0 false true
13 job-timeout system 9 TIMED_ 0 0:0 false true
1 main main 5 TIMED_ 0 0:0 false false
14 nioEventLoopGroup-2-1 system 10 RUNNAB 0 0:0 false false
18 nioEventLoopGroup-2-2 system 10 RUNNAB 0 0:0 false false
23 nioEventLoopGroup-2-3 system 10 RUNNAB 0 0:0 false false
15 nioEventLoopGroup-3-1 system 10 RUNNAB 0 0:0 false false
Memory used total max usage GC
heap 32M 155M 1820M 1.77% gc.ps_scavenge.count 4
ps_eden_space 14M 65M 672M 2.21% gc.ps_scavenge.time(m 166
ps_survivor_space 4M 5M 5M s)
ps_old_gen 12M 85M 1365M 0.91% gc.ps_marksweep.count 0
nonheap 20M 23M -1 gc.ps_marksweep.time( 0
code_cache 3M 5M 240M 1.32% ms)
Runtime
os.name Mac OS X
os.version 10.13.4
java.version 1.8.0_162
java.home /Library/Java/JavaVir
tualMachines/jdk1.8.0
_162.jdk/Contents/Hom
e/jre
arthas常用命令文档:arthas常用命令文档
常见的调优方法
trace 方法
所在文件全路径 方法名
比如说我想查看ImpDocServiceImpl 的add方法
例:trace com.bxkc.service.impl.backend.win.database.WinDatabaseServiceImpl winProjectDbList
调用接口后,出现如下,可以清晰的看到每行代码的耗时
thread
用thread命令列出线程的信息
用thread -b查看死锁
dashboard
这个我们可以用dashboard命令来动态查看内存情况
如果内容使用率在不断上升,而且gc后也不下降,后面还发现gc越来越频繁,很可能就是内存泄漏了。
这个时候我们可以直接用heapdump命令把内存快照dump出来,作用和jmap工具一样
watch——方法执行数据观测
代码演示:
[root@172-21-21-98 ~]$ # 初次启动arthas
[root@172-21-21-98 ~]$ docker exec -it x-service-xxxx /bin/sh -c "wget https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar --repo-mirror aliyun --use-http"
[INFO] arthas-boot version: 3.4.8
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 6 x-service-xxxx-1.0.0-SNAPSHOT.jar
# 输入服务的编号:1,按enter,
1
[INFO] arthas home: /app/arthas
[INFO] Try to attach process 6
[INFO] Attach process 6 success.
# 成功attch到x-service-xxxx服务
# 开始播放arthas的开机动画😋...
[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.4.8
pid 6
time 2021-03-25 15:57:08
[arthas@6]$
[arthas@6]$ # 输入命令,watch 类名(q全限定名,即类的完整路径) 方法名 OGNL表达式(查看入参)-x 2 (查看对象的层级)
[arthas@6]$ watch com.x.service.xxxx.service.impl.DemandTemplateServiceImpl saveMiddleTempList "{params}" -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 2 , method count: 1) cost in 513 ms, listenerId: 5
# 程序等待...
# 去页面点击一下,触发函数
method=com.x.service.xxxx.service.impl.DemandTemplateServiceImpl.saveMiddleTempList location=AtExceptionExit
ts=2021-03-27 12:31:12; [cost=73.741218ms] result=@ArrayList[
@Object[][
@ArrayList[isEmpty=false;size=9],
@String[1008],
@String[group1/M00/02/08/rBUVDGBYZxOAV2ZHAAi3CxHKUZo90.docx],
@String[测试拆分场景.docx],
@String[XQ20210322-0062],
],
]
# 好,看到了 按Ctrl+C中断监听
不用发版,热更新代码✨
retransform——加载外部的.class
文件,retransform到JVM里
jad——查看已加载类的源码/ 查看类中某个方法的源码。
代码演示:
[root@172-21-21-98 ~]$ # 1. 将class文件上传到服务器,放在/tmp目录下 即:/tmp/DemandTemplateServiceImpl.class
[root@172-21-21-98 ~]$ # 此过程略
[root@172-21-21-98 ~]$
[root@172-21-21-98 ~]$ # 2. 将class文件拷贝到docker容器中
[root@172-21-21-98 ~]$ docker cp /tmp/DemandTemplateServiceImpl.class x-service-xxxx:/tmp/
[root@172-21-21-98 ~]$
[root@172-21-21-98 ~]$ # 3. 再次启动arthas
[root@172-21-21-98 ~]$ docker exec -it x-service-xxxx /bin/sh -c "java -jar arthas-boot.jar"
[INFO] arthas-boot version: 3.4.8
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 6 x-service-xxxx-1.0.0-SNAPSHOT.jar
# 4. 输入1 ,attch到x-service-xxxx服务
1
[INFO] arthas home: /app/arthas
[INFO] Try to attach process 6
[INFO] Attach process 6 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.4.8
pid 6
time 2021-03-25 15:57:08
[arthas@6]$ # 5. retransform热更新代码
[arthas@6]$ retransform /tmp/DemandTemplateServiceImpl.class
retransform success, size: 1, classes:
com.x.service.xxxx.service.impl.DemandTemplateServiceImpl
[arthas@6]$
[arthas@6]$ # 6. 查看方法的代码,确定是否修改成功 (可选参数 --source-only (只查看源代码) --lineNumber false (不显示行号) )
[arthas@6]$ jad --source-only --lineNumber false com.x.service.xxxx.service.impl.DemandTemplateServiceImpl saveMiddleTempList
private void saveMiddleTempList(List<WordDirectoryInfo> allSceneList, String tenantsNo, String filePath, String fileName, String bizReqrmntNo) {
ArrayList<MiddleTempEntity> insertList = new ArrayList<MiddleTempEntity>(allSceneList.size());
for (WordDirectoryInfo dir : allSceneList) {
String inno = this.generateNo.getNo(tenantsNo, GenerateNoPrefixEnum.DEMAND_SCENARIO_MIDDLE.getValue(), 18);
MiddleTempEntity entity = new MiddleTempEntity(tenantsNo, inno, dir.getText(), null, fileName, filePath, bizReqrmntNo);
entity.setCrtTm(LocalDateTime.now().toString());
entity.setModfyTm(LocalDateTime.now().toString());
insertList.add(entity);
}
log.debug("insertList:{}", (Object)((Object)insertList).toString());
try {
this.middleTempMapper.insertList(insertList);
}
catch (Exception e) {
log.error(">>>saveMiddleTempList-数据库写入失败!", (Throwable)e);
throw new CommonException("文件检查失败:数据库异常!");
}
}
[arthas@6]$
[arthas@6]$ # 7. 热更新成功!
评论区