arthas诊断工具的使用(基础篇)

9,605 阅读3分钟

1. arthas简介

arthas 是一款用于java程序诊断的跨平台开源工具,由Alibaba开源、维护,集成了jvm跟踪查看、反编译、热更新、热加载、代码执行追踪等功能,方便开发者排查定位线上问题、调优等。

官方文档中罗列出arthas可协助排查的场景如下:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到JVM的实时运行状态?
  7. 怎么快速定位应用的热点,生成火焰图?

2. 安装与基本使用

2.1 下载与安装

github release:github.com/alibaba/art…

  • 快速安装
# 下载jar包
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

# 第一次打开时,会下载关联包,下载速度慢,可使用aliyun镜像
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
  • 安装shell命令
curl -L https://arthas.aliyun.com/install.sh | sh

该命令会下载启动脚本文件as.sh到当前目录,可配置环境变量快速启动

推荐采用快速安装方式,通过jar包方式启动更灵活,多平台之间的操作体验也是一致的。

2.2 基本使用

arthas运行结构.png

arthas以java进程作为基本的操作单元,启动arthas后,会列出所有的java进程,选择编号后,会启动一个arthas server,后续可创建多个会话连接到arthas server,连接上后,即可通过arthas提供的命令来对java进程进行诊断。

2.2.1 启动并连接到java进程

# 启动arthas
cd arthasPath
java -jar arthas-boot.jar

启动arthas后,会罗列出所有的java进程,及对应的编号

Arthas script version: 3.4.6
[INFO] JAVA_HOME: /Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home
[INFO] Process 3983 already using port 3658
[INFO] Process 3983 already using port 8563
Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 1795
  [2]: 2484 com.intellij.database.remote.RemoteJdbcServer
  [3]: 1828 org.jetbrains.jps.cmdline.Launcher
  [4]: 533
  [5]: 1290 com.intellij.database.remote.RemoteJdbcServer
  [6]: 3982 org.jetbrains.jps.cmdline.Launcher
  [7]: 3983 top.learningwang.arthasdemo.ArthasDemoApplication

输入想诊断的java进程对应的编号并按下enter键,等待arthas server创建,会话建立后,即可通过arthas命令进行操作

Attaching to 3983 using version /Users/wangjingbiao/software/arthas...

real	0m0.202s
user	0m0.369s
sys	0m0.045s
Attach success.
telnet connecting to arthas server... current timestamp is 1613983174
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'


wiki      https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version   3.4.6
pid       3983
time      2021-02-22 16:15:16

[arthas@3983]$

2.2.2 执行arthas命令

启动arthas会话后,即可输入执行arthas支持的命令。 目前支持的命令列表:arthas.aliyun.com/doc/command…

arthas命令集.png

下文会详细展示常用命令、及特定场景的命令操作。

2.2.3 退出arthas

  • quit 退出当前 Arthas 客户端,其他 Arthas 客户端不受影响

  • exit 等同于quit

  • stop 关闭 Arthas 服务端,所有 Arthas 客户端全部退出

3. arthas 常用命令

dashboard

dashboard 命令官方文档

  • 功能 展示当前java进程的实时数据(线程、gc、jvm内存占用等)信息

  • 命令使用示例

# 展示当前java进程 线程cpu占用、gc信息、jvm信息等,5s刷新一次
dashboard

# 刷新10次后终止,默认5s刷新一次
dashboard -n 10

# 2s刷新一次
dashboard -i 2000

# 2s刷新一次,共刷新10次
dashboard -i 2000 -n 10

# 2s刷新一次,共刷新10次,并保存到文件中,用于后续分析
dashboard -i 2000 -n 10 | tee /data/dashback_2021_02_21.txt

sysprop、sysenv、jvm

sysprop命令官方文档
sysenv命令官方文档
jvm命令官方文档

  • 功能 sysprop:查看当前JVM的系统属性(System Property) sysenv:查看当前JVM的环境属性(System Environment Variables) jvm:查看当前jvm信息
# 查看jvm信息
jvm

# 查看当前jvm的所有系统属性
sysprop

# 查看具体的某个系统属性
sysprop java.class.version

# 模糊匹配某些系统属性
sysprop | grep java.class.

# sysenv用法同sysprop

thread

thread 命令官方文档

  • 功能 展示当前java进程详细的线程信息

  • 命令使用示例

# 展示所有的线程列表
thread
thread-all

# cpu使用率采样修改为2s,默认为200ms
thread -i 2000

# 打印出cpu使用率前5的线程详情,即比较繁忙的线程,cpu使用率采样统计方式请参考官方文档说明
thread -n 5

# 打印id为5的线程详情
thread 5

# 根据状态过滤线程数据(NEW, RUNNABLE, TIMED_WAITING, WAITING, BLOCKED, TERMINATED)
thread --state RUNNABLE
thread |grep RUNNABLE

# 列出持有某个锁,阻塞其他线程最多的线程,排查死锁
thread -b

classloader

classloader 命令官方文档

  • 功能 查看类加载器信息

  • 命令使用示例

# 查看类加载器,及加载信息
classloader

# 查看类加载器hash、parent信息
classloader -l

# 查看类加载器之间的继承树
classloader -t

# 列出所有类加载器及加载的类
classloader -a

# 查看URLClassLoader实际的urls
classloader -c hashcode

sc

sc命令官方文档

  • 功能 查看jvm中某个类的信息

  • 命令使用示例

# 模糊匹配类信息,支持正则表达式,如果是接口,还会列出所有的实现类
sc java.lang.String*

# 模糊匹配类信息,限制最多匹配到的数量,默认100
sc java.lang.String* -n 2

# 查看类详情信息
sc -d java.lang.String

# 指定类加载器查看类信息
sc -c hashcode java.lang.String*

# 查看类详情信息,包含field信息
sc -d -f java.lang.String

sm

sm命令官方文档

  • 功能 查询jvm中某个类的方法信息

  • 命令使用示例

# 查看某个类下所有的方法信息
sm top.learningwang.arthasdemo.controller.VipUserController

# 查看某个类下的某个方法信息
sm top.learningwang.arthasdemo.controller.VipUserController dropVipUser

# 查看方法详情
sm -d top.learningwang.arthasdemo.controller.VipUserController dropVipUser

# 指定类加载器查看类下某个方法的详细信息
sm -c hashcode -d top.learningwang.arthasdemo.controller.VipUserController dropVipUser

monitor

monitor命令官方文档

  • 功能 统计方法一段周期内的执行情况

  • 命令使用示例

# 统计某个类某个方法的执行信息,默认60s为统计周期
monitor top.learningwang.arthasdemo.controller.VipUserController helloUser

# 统计某个类下某个方法的执行信息,10s为统计周期,统计3次
monitor top.learningwang.arthasdemo.controller.VipUserController helloUser -c 10 -n 3

# 统计某个类下某个方法的执行信息,限定参数值,5s为统计周期
monitor top.learningwang.arthasdemo.controller.VipUserController helloUser "params[0].length<5" -c 5

watch

watch命令官方文档

  • 功能 观测某方法执行的详情

  • 命令使用示例

# 观测某方法的执行详情,支持ognl表达式输出观测结果
watch *VipUserController helloUser '{clazz,method,isReturn,isThrow,params,target,returnObj,throwExp}'
:<<!
常用表达式
target : the object
clazz : the object's class
method : the constructor or method
params : the parameters array of method
params[0..n] : the element of parameters array
returnObj : the returned object of method
throwExp : the throw exception of method
isReturn : the method ended by return
isThrow : the method ended by throwing exception
#cost : the execution time in ms of method invocation
!

# 限制观测执行次数
watch *VipUserController helloUser -n 2

# 设置观测结果遍历深度
wathch *VipUserController helloUser -x 2

# 只观测执行成功
watch *VipUserController helloUser -s

# 只观测执行失败
watch *VipUserController helloUser -e

# 同时观测方法执行前、方法执行后结果
watch *VipUserController helloUser -b -f

# 观测方法执行异常时,详细的异常栈信息
watch *VipUserController helloUser '{throwExp}' -e -x 2

# 观测方法执行时间大于200ms的信息
watch *VipUserController helloUser '#cost>100'

trace

trace命令官方文档

  • 功能 追踪方法内部调用路径,并输出方法路径上的每个节点上耗时(只会打印到第一层)

  • 命令使用示例


# 观测方法内部调用顺序及耗时
trace top.learningwang.arthasdemo.controller.VipUserController helloUser

# 观测方法内部调用顺序及耗时,只观测3次
trace top.learningwang.arthasdemo.controller.VipUserController helloUser -n 3

# 观测方法内部调用顺序及耗时,包含jdk内部方法
trace top.learningwang.arthasdemo.controller.VipUserController helloUser --skipJDKMethod false

# 限制观测范围,第一个参数长度大于10
trace top.learningwang.arthasdemo.controller.VipUserController helloUser params[0].length>=10

# 限制观测范围,执行耗时大于100ms
trace top.learningwang.arthasdemo.controller.VipUserController helloUser '#cost>100'

# trace 同时多个类的多个方法
trace -E com.test.ClassA|org.test.ClassB method1|method2|method3

stack

stack命令官方文档

  • 功能 输出当前方法被调用的调用路径

  • 命令使用示例


# 观测某方法的调用栈
stack top.learningwang.arthasdemo.service.VipUserService getVipUserByCardNo

# 观测某方法的调用栈,3次
stack top.learningwang.arthasdemo.service.VipUserService getVipUserByCardNo -n 3

# 观测某方法耗时大于100ms时的调用栈
stack top.learningwang.arthasdemo.service.VipUserService getVipUserByCardNo '#cost>100'

headdump

headdump命令官方文档

  • 功能 生成headdump文件到执行目录,用于分析对象内存占用等问题。 常用分析headdump文件的软件:Eclipse Memory Analyzer(MAT)

  • 命令使用示例


# 生成headdump文件到指定目录
heapdump /tmp/dump.hprof

# 只dump live对象
heapdump --live /tmp/dump.hprof

4. 场景实战

4.1 修改日志级别

目前存在web服务,日志级别为error,解决线上问题时,需要看info中打印的信息,来排查数据问题,可按照如下方式进行修改日志级别。

# 查找到具体的类信息
sc -d *VipUserController

# 使用ognl表达式查看日志属性的信息,判断日志级别
ognl -c classLoaderHash "@top.learningwang.arthasdemo.controller.VipUserController@logger"

# 使用ognl表达式修改日志级别
ognl -c classLoaderHash "@top.learningwang.arthasdemo.controller.VipUserController@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)"

# 再次查看日志级别,判断是否修改成功
ognl "@top.learningwang.arthasdemo.controller.VipUserController@logger"

# 修改全局日志级别
ognl -c classLoaderHash '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'

4.2 更新代码、热加载

线上代码有一行出现了问题,想不重启,修改代码后,实现热加载

# 反编译源码,并保存到文件中
jad --source-only top.learningwang.arthasdemo.controller.VipUserController > /data/VipUserController.java

编辑代码,修改hello为hello2

# 根据源码,编译出class文件,并保存到执行目录
mc -c 18b4aac2 /data/VipUserController.java -d /data/

# 重新加载编译好的class文件
redefine -c 18b4aac2 /data/top/learningwang/arthasdemo/controller/VipUserController.class

访问接口,测试热加载结果

4.3 排查函数调用异常

观测方法执行异常具体信息,入参、出参等

# 观测方法执行异常时,详细的异常栈信息
watch *VipUserController helloUser '{throwExp}' -e -x 2

4.4 arthas 后台异步执行诊断任务

执行dashboard、watch、trace等命令时,可将命令执行挂起,将结果输出到文件中,供后续分析,不影响其他命令执行。

# 后台执行dashboard,并输出结果到文件中
dashboard >> /data/dash.log &

# 查看当前执行中的后台任务
jobs

# 终止后台任务
kill jobid

4.5 排查方法执行效率问题

线上某个接口执行慢,无法确定是哪一段代码的问题。

# 观测某几个方法,执行耗时大于100ms的调用栈
trace -E com.test.ClassA|org.test.ClassB method1|method2|method3 '#cost>100'

可根据各方法执行耗时,缩小排查范围。

5. 远程诊断

5.1 webConsole

启动arthas Server时,会默认启动webConsole服务,默认端口号 3658,对端口访问放开权限后,外部即可直接通过访问 http://ip:port方式在浏览器中远程执行arthas命令

arthasWebConsole.png

5.2 tunnel server

arthass tunnel server 官方文档

arthas 提供了Springboot starter,配置到项目中后,启动项目时,会启动一个arthas tunnnel server

<!-- springboot 依赖-->
<dependency>
	<groupId>com.taobao.arthas</groupId>
	<artifactId>arthas-spring-boot-starter</artifactId>
	<version>${arthas.version}</version>
</dependency>

springboot arthas配置项

启动服务后,网络可连通的情况下,即可实现arthas远程诊断

java -jar arthas-boot.jar 'ws://ip:port/ws'

附1:源码地址

演示命令时用到的springboot web服务源码

附2:学习资料

  1. arthas官方文档
  2. 阿里云arthas在线教程
  3. OGNL表达式官方指南
  4. 强大的OGNL
  5. Java动态追踪技术探究