Arthas基本用法指南
INFO
线上问题诊断利器-Arthas
1、arthas介绍
1.1、arthas是什么
Arthas 是阿里一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率
1.2、arthas常用场景
arthas主要应用于以下这些场景中:
这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
是否有一个全局视角来查看系统的运行状况?
有什么办法可以监控到 JVM 的实时运行状态?
怎么快速定位应用的热点,生成火焰图?
怎样直接从 JVM 内查找某个类的实例?
2、下载安装
2.1、下载
github下载地址:https://github.com/alibaba/arthas/releases
下载指定版本的arthas-bin.zip文件即可,解压后内容如下:

其中核心文件是arthas-boot.jar和as.sh或者as.bat。其中arthas-boot.jar文件可以直接通过java -jar arths-boot.jar的方式运行,as.sh是Linux或者mac下的运行文件,as.bat是Windows下的运行文件,但是这两个文件以脚本方式运行的话前提是需要安装telnet
2.2、运行
我这里是通过java -jar arthas-boot.jar方式运行的,arthas启动后需要attach到某个Java进程里边,通过选择对应进程名称对应的编号即可,如下:

我这里进入的是com.study.arthas.ArthasDemoApplication对应的3
3、操作
虽然前面介绍了arthas使用的一些常用场景,但是这里还是想从实际开发工作中可能会遇到的一些场景重点介绍几个重要的命令
3.1、dashboard监控
执行命令:
# dashboard [-i 数值] [-n 数值],如下命令表示5s刷新一次
dashboard -i 5000

第一部分打印出来的是线程信息,包括线程名称、线程组、线程状态、线程占用cpu资源情况等等
第二部分打印出来的是内存消耗情况,包括heap(s0、s1、以及老年代)、nonheap
第三部分是运行时环境,包括操作系统信息和jvm信息
3.2、heapdump堆栈信息
这个命令和jmap的heap dump相似,只不过用法不同
jmap dump堆栈信息的方式:jmap -dump:live,format=b,file=heapdump.hprof <pid>
Arthas dump堆栈信息的方式:heapdump --live arthas-output/dump.hprof 看起来也更简洁了
3.3、jad反编译
有时候需要查看已经被加载的class文件的内容,jad命令就很有用了

3.4、jvm查看jvm各参数
输出的内容太多了,就不贴了
3.5、logger日志相关
3.5.1、查看日志级别
如果要查看日志的级别,比如确认线上或测试环境的日志级别
logger --name ROOT

或者查看某个类或者包的日志级别
logger --name com.study.arthas.web.TestController

3.5.2、动态修改日志级别
上面看到com.study.arthas.web.TestController这个类的日志级别是debug,如果是线上环境全部都是info级别,现在要到线上去定位问题,想要放开这个类的所有debug日志怎么办呢,不可能修改配置文件然后重启吧?
这个时候就可以用arthas动态地去调整,如下所示就可以动态地将对应的类的日志级别调整为debug级别:
logger --name com.study.arthas.web.TestController --level debug
当然也可以批量指定某个ClassLoader加载的所有类的日志级别,用-c ClassLoader的哈希值参数
3.6、memory查看内存信息

3.7、monitor监控
monitor可以监控方法的执行情况,比如我需要看一下自己写的方法的执行效率,则monitor是一个很方便工具,简单实用如下:

方法拥有一个命名参数 [c:],意思是统计周期(cycle of output),拥有一个整型的参数数值。上面每一行打印统计的都是5s之内对应的sayHello方法执行的统计信息,比如调用次数、成功次数、失败次数、平均响应时间rt、失败率
3.8、搜索加载的类或方法
3.8.1、sc(search class)搜索加载的类

3.8.2、sm(search method)搜索某个类的方法

3.9、stack查看调用路径
很多时候我们都知道一个方法被执行,但这个方法被执行的路径非常多,或者你根本就不知道这个方法是从那里被执行了,此时你可以用到stack 命令

这样就可以看出来是从TestController的26行调的SayHelloService里边的sahHello方法。其中的-n 参数表示监听1次调用
不仅如此,stack还可以使用ognl表达式过滤,如我只监听参数为“Navy”的调用

只有参数为Navy的时候才会往下执行

也可以根据方法的调用耗时过滤,如表达式"#cost>5"
3.10、thread线程管理
3.10.1、thread命令查看线程状态
直接执行thread命令可以查看线程的状态

没有参数时打印第一页的线程,加--all参数打印所有线程
3.10.2、打印最忙的线程

3.10.3、打印线程堆栈信息
使用命令thread 线程ID打印某个指定线程的堆栈信息,显示信息如上图
3.10.4、查看某个状态的线程
如下所示,查看waiting状态的所有线程

3.11、trace查看调用路径
trace命令可以搜索对应方法的调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路,顾名思义有点链路追踪的意思

示例监听了1次调用,并统计除了方法的调用路径,包括嵌套的方法的执行耗时。
但是这里可以看到下面两个方法调用的耗时相加并不等于上面的0.524538ms,这里还有一些隐藏的方法调用没有打印出来,比如jdk自带的方法被忽略了,可以通过增加--skipJDKMethod false参数打印出来。以及还有一些非函数调用(如i++)和jvm停顿时间消耗等等。
此外,trace和stack类似,也可以使用ognl表达式过滤
3.12、tt方法执行时空隧道
tt是TimeTunnel的简称,译为方法执行数据的时空隧道。记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。这一点对我们线上问题排查将会是非常有用的,因为有时候我们并没有打印对应的入参出参等信息
3.12.1、记录调用信息

这里的-t参数表示记录下对应方法每次的调用情况,-n参数表示记录1次
tt命令也支持ognl表达式

3.12.2、检索调用记录

3.12.3、查看调用明细

各项信息顾名思义,可以看到方法是否是正常返回,参数值和返回值信息等。
3.12.4、重新调用
有的时候我们在联调的时候调用需要前端配合在前端操作,如果我们稍稍做了一些调整之后,可能需要前端系统重新触发一次我们的调用,那还需要联系前端,为了提升效率其实我们自己就可以操作,这里用到tt命令的重新调用机制,只需要通过-p参数就行

通过 --replay-times 指定 调用次数,通过 --replay-interval 指定多次调用间隔(单位 ms, 默认 1000ms)。
3.12.5、注意事项
- ThreadLocal 信息丢失
很多框架偷偷的将一些环境变量信息塞到了发起调用线程的 ThreadLocal 中,由于调用线程发生了变化,这些 ThreadLocal 线程信息无法通过 Arthas 保存,所以这些信息将会丢失。
- 引用的对象
需要强调的是,tt 命令是将当前环境的对象引用保存起来,但仅仅也只能保存一个引用而已。如果方法内部对入参进行了变更,或者返回的对象经过了后续的处理,那么在 tt 查看的时候将无法看到当时最准确的值。这也是为什么 watch 命令存在的意义
3.13、watch函数执行数据观测
功能类似trace、tt等,但主要目标是为了观察方法的执行情况,比如方法的请求入参、出参以及异常信息等
对应的观察表达式,默认值是{params, target, returnObj}

这里的-x参数表示输出结果的属性遍历深度,默认为 1,最大值是 4,-n参数表示监听调用的次数
可以看到hello方法的执行耗时(cost),结果(result)信息里边第一个信息是入参(Navy),出参(hello Navy),目标信息(TestController)
注意事项:
- watch 命令定义了 4 个观察事件点,即
-b函数调用前,-e函数异常后,-s函数返回后,-f函数结束后 - 4 个观察事件点
-b、-e、-s默认关闭,-f默认打开,当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出 - 这里要注意
函数入参和函数出参的区别,有可能在中间被修改导致前后不一致,除了-b事件点params代表函数入参外,其余事件都代表函数出参 - 当使用
-b时,由于观察事件点是在函数调用前,此时返回值或异常均不存在 - 在 watch 命令的结果里,会打印出
location信息。location有三种可能值:AtEnter,AtExit,AtExceptionExit。对应函数入口,函数正常 return,函数抛出异常。



