大家好,我是爱吃鱼的程序员,一个渴望在互联网行业做到C位的程序员。可柔可刚,点赞则柔,白嫖则刚!看完记得给我来个三连哦!欢迎私信!
java排查神器Arthas -日常用法分析
前言
有时候线上出现问题,我们需要迫切的找寻解决方法,加日志再上线?查看修改的功能是否成功上线?现在有了Arthas一切都是那么简单。
我们看下官网的描述:Arthas 是Alibaba开源的Java诊断工具
,深受开发者喜爱。
- 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到JVM的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。
学习
推荐 按照官方文档结合在线教程。本文档记录一下日常的用法,便于快速查找。
日常用法
wget https://arthas.aliyun.com/arthas-boot.jar
快速下载
- 监控方法调用 monitor/watch/trace相关
请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行 stop 或将增强过的类执行 reset 命令。
- monitor——方法执行监控
- watch——方法执行数据观测
- trace——方法内部调用路径,并输出方法路径上的每个节点上耗时
- stack——输出当前方法被调用的调用路径
- tt——方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
watch
让你能方便的观察到指定方法的调用情况。能观察到的范围为:返回值、抛出异常、入参,通过编写 OGNL 表达式进行对应变量的查看。
参数:
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
express 观察表达式
condition-express 条件表达式
-b 在方法调用之前观察
-e 在方法异常之后观察
-s 在方法返回之后观察
-f 在方法结束之后(正常返回和异常返回)观察 默认开启
-E 开启正则表达式匹配,默认为通配符匹配
-x 指定输出结果的属性遍历深度,默认为 1
-n 执行的次数
特别说明:
- watch 命令定义了4个观察事件点,即
-b
方法调用前,-e
方法异常后,-s
方法返回后,-f 方法结束后 - 4个观察事件点
-b、-e、-s
默认关闭,-f 默认打开
,当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出 - 这里要注意方法入参和方法出参的区别,有可能在中间被修改导致前后不一致,除了
-b
事件点 params 代表方法入参外,其余事件都代表方法出参 - 当使用
-b
时,由于观察事件点是在方法调用前,此时返回值或异常均不存在 - -x 代表输出结果的深度 ,默认为 1
使用参考
- 常规用法
watch class method {params,returnObj} -x 1 '#cost>200'
观察表达式 { }
可以包裹结果集 ,
分割。输出的表达式变量定义
params 代表参数数组 ,returnObj代表返回值
过滤耗时大于200ms的
- 特殊用法
1.调用第一个参数的方法或属性
watch class method "{params[0].length()}" -x 1
watch class method "{params[0].{ #this.length()}}" -x 1
2.按照条件过滤
watch class method "{params[0].{?#this.length()>7}}" -x 1
字符串长度 > 7 才会输出 params[0] 的值
3.过滤后统计
1 | kotlin复制代码`watch class method "{params[0].{?#this.length()>9}.size()}" -x 1` |
trace
trace 命令能主动搜索 class-pattern/method-pattern
对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。
参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式
-E 开启正则表达式匹配,默认为通配符匹配
-n 命令执行次数
#cost 方法执行耗时
使用参考
1.常规用法
trace class method '#cost>100' -n 1
过滤大于100ms的调用链,只输出一次
2.特殊用法
trace命令只会trace匹配到的函数里的子调用,并不会向下trace多层。因为trace
是代价比较贵
的,多层trace可能会导致最终要trace的类和函数非常多。
动态trace
3.3.0 版本后支持。
打开终端1,trace run函数,可以看到打印出 listenerId
: 1:
1 | bash复制代码[arthas@59161]$ trace demo.MathGame run |
现在想要深入子函数primeFactors
,可以打开一个新终端2,使用telnet localhost 3658
连接上arthas
,再trace primeFactors
时,指定listenerId
。
再查看终端1,可以发现trace的结果增加了一层,打印了primeFactors函数里的内容
1 | bash复制代码`---ts=2020-07-09 16:49:29;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69 |
通过指定listenerId的方式动态trace,可以不断深入。另外 watch/tt/monitor等命令也支持类似的功能。
tt
方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。
watch
虽然很方便和灵活,但需要提前想清楚观察表达式的拼写,这对排查问题而言要求太高,因为很多时候我们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜测。
这个时候如果能记录下当时方法调用的所有入参和返回值、抛出的异常会对整个问题的思考与判断非常有帮助。
于是乎,TimeTunnel
命令就诞生了。
参数说明:
-n 执行次数
#cost 过滤耗时
-t tt 命令有很多个主参数,-t 就是其中之一。这个参数的表明希望记录下类 *Test 的 print 方法的每次执行情况。
-s 可以跟条件表达式 进行筛选
-l 列出所有的记录
记录说明
表格字段 | 字段解释 |
---|---|
INDEX | 时间片段记录编号,每一个编号代表着一次调用,后续tt还有很多命令都是基于此编号指定记录操作,非常重要。 |
TIMESTAMP | 方法执行的本机时间,记录了这个时间片段所发生的本机时间 |
COST(ms) | 方法执行的耗时 |
IS-RET | 方法是否以正常返回的形式结束 |
IS-EXP | 方法是否以抛异常的形式结束 |
OBJECT | 执行对象的hashCode(),注意,曾经有人误认为是对象在JVM中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体 |
CLASS | 执行的类名 |
METHOD | 执行的方法名 |
使用参考
tt -t class method '#cost>100'
1 | bash复制代码 INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD |
查看调用信息
对于具体一个时间片的信息而言,你可以通过 -i
参数后边跟着对应的 INDEX
编号查看到他的详细信息。tt -i 1003
1 | bash复制代码$ tt -i 1003 |
tt -l
查看我们现有的记录
1 | bash复制代码$ tt -l |
我需要筛选出 primeFactors
方法的调用信息tt -s
1 | bash复制代码$ tt -s 'method.name=="primeFactors"' |
stack
很多时候我们都知道一个方法被执行,但这个方法被执行的路径非常多,或者你根本就不知道这个方法是从那里被执行了,此时你需要的是 stack 命令。
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式
-E 开启正则表达式匹配,默认为通配符匹配
-n 执行次数限制
使用参考
stack demo.MathGame primeFactors ’#cost>5‘
输出调用链,按照执行时间过滤
stack demo.MathGame primeFactors 'params[0]<0' -n 2
按照条件表达式进行过滤
monitor
参数说明
方法拥有一个命名参数 [c:],意思是统计周期(cycle of output),拥有一个整型的参数值
参数名称 | 参数说明 |
---|---|
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
-E | 开启正则表达式匹配,默认为通配符匹配 |
-c | 统计周期,默认值为120秒 |
-b | 在方法调用之前计算condition-express |
监控的维度说明
监控项 | 说明 |
---|---|
timestamp | 时间戳 |
class | Java类 |
method | 方法(构造方法、普通方法) |
total | 调用次数 |
success | 成功次数 |
fail | 失败次数 |
rt | 平均RT |
fail-rate | 失败率 |
使用参考
monitor -c 5 demo.MathGame primeFactors
每5秒统计一次
1 | bash复制代码$ monitor -c 5 demo.MathGame primeFactors |
计算条件表达式过滤统计结果(方法执行完毕之后)
1 | bash复制代码monitor -c 5 demo.MathGame primeFactors "params[0] <= 2" |
2.jvm相关
- dashboard——当前系统的实时数据面板
- thread——查看当前 JVM 的线程堆栈信息
- jvm——查看当前 JVM 的信息
- sysprop——查看和修改JVM的系统属性
- sysenv——查看JVM的环境变量
- vmoption——查看和修改JVM里诊断相关的option
- perfcounter——查看当前 JVM 的Perf Counter信息
- logger——查看和修改logger
- getstatic——查看类的静态属性
- ognl——执行ognl表达式
- mbean——查看 Mbean 的信息
- heapdump——dump java heap, 类似jmap命令的heap dump功能
dashboard
当前系统的实时数据面板,按 ctrl+c 退出。
命令
dashboard -i 5000 -n 10
-i
刷新实时数据的时间间隔 (ms),默认5000ms-n
刷新数据的次数
数据说明
- ID: Java级别的线程ID,注意这个ID不能跟jstack中的nativeID一一对应。
- NAME: 线程名
- GROUP: 线程组名
- PRIORITY: 线程优先级, 1~10之间的数字,越大表示优先级越高
- STATE: 线程的状态
- CPU%: 线程的cpu使用率。比如采样间隔1000ms,某个线程的增量cpu时间为100ms,则cpu使用率=100/1000=10%
- DELTA_TIME: 上次采样之后线程运行增量CPU时间,数据格式为秒
- TIME: 线程运行总CPU时间,数据格式为分:秒
- INTERRUPTED: 线程当前的中断位状态
- DAEMON: 是否是daemon线程
thread
查看当前线程信息,查看线程的堆栈
参数名称 | 参数说明 |
---|---|
id | 线程id |
-n | 指定最忙的前N个线程并打印堆栈 |
-b | 找出当前阻塞其他线程的线程 |
-i | 指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200 |
–all | 显示所有匹配的线程 |
–state | 查看指定状态的线程 |
常用操作
thread -b
, 找出当前阻塞其他线程的线程
有时候我们发现应用卡住了, 通常是由于某个线程拿住了某个锁, 并且其他线程都在等待这把锁造成的。 为了排查这类问题, arthas提供了thread -b, 一键找出那个罪魁祸首
thread -i 1000
: 统计最近1000ms内的线程CPU时间。
thread -n 3 -i 1000
: 列出1000ms内最忙的3个线程栈
thread --state WAITING
列出等待态的线程
jvm
查看当前JVM信息
使用参考
1 | bash复制代码$ jvm |
THREAD相关
- COUNT: JVM当前活跃的线程数
- DAEMON-COUNT: JVM当前活跃的守护线程数
- PEAK-COUNT: 从JVM启动开始曾经活着的最大线程数
- STARTED-COUNT: 从JVM启动开始总共启动过的线程次数
- DEADLOCK-COUNT: JVM当前死锁的线程数
文件描述符相关
- MAX-FILE-DESCRIPTOR-COUNT:JVM进程最大可以打开的文件描述符数
- OPEN-FILE-DESCRIPTOR-COUNT:JVM当前打开的文件描述符数
heapdump
dump java heap, 类似jmap
命令的heap dump
功能。
使用参考
dump到指定文件
1 | bash复制代码[arthas@58205]$ heapdump /tmp/dump.hprof |
只dump live对象
1 | bash复制代码[arthas@58205]$ heapdump --live /tmp/dump.hprof |
dump到临时文件
1 | bash复制代码[arthas@58205]$ heapdump |
sysprop 和 sysenv
sysprop
查看和修改当前JVM的系统属性
(System Property)sysenv
查看当前JVM的环境属性
(System Environment Variables)
sysprop 和 sysenv使用方式类似
使用参考
1 | bash复制代码$ sysprop |
查看单个属性
支持通过TAB键自动补全
1 | bash复制代码$ sysprop java.version |
修改单个属性
1 | bash复制代码$ sysprop user.country |
logger
查看logger
信息,更新logger level
使用参考
查看所有logger信息
1 | bash复制代码[arthas@2062]$ logger |
从appenders
的信息里,可以看到
CONSOLE
logger的target是System.out
APPLICATION
logger是RollingFileAppender
,它的file是app.log
ASYNC
它的appenderRef是APPLICATION
,即异步输出到文件里
查看指定的logger信息
按名字查找
1 | bash复制代码[arthas@2062]$ logger -n org.springframework.web |
指定classloader查找
注意hashcode
是变化的,需要先查看当前的ClassLoader信息,提取对应ClassLoader的hashcode
。
如果你使用-c
,你需要手动输入hashcode:-c <hashcode>
1 | bash复制代码[arthas@2062]$ logger -c 2a139a55 |
更新 logger level
1 | bash复制代码[arthas@2062]$ logger --name ROOT --level debug |
指定classloader更新 logger level
默认情况下,logger命令会在SystemClassloader
下执行,如果应用是传统的war
应用,或者spring boot fat jar启动的应用,那么需要指定classloader。
1 | bash复制代码[arthas@2062]$ logger -c 2a139a55 --name ROOT --level debug |
- class/classloader相关
jad
反编译指定已加载类的源码
jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于你理解业务逻辑;
- 在 Arthas Console 上,反编译出来的源码是带语法高亮的,阅读更方便
- 当然,反编译出来的 java 代码可能会存在语法错误,但不影响你进行阅读理解
使用参考
编译java.lang.String
1 | bash复制代码$ jad java.lang.String |
反编译时只显示源代码
默认情况下,反编译结果里会带有ClassLoader信息,通过--source-only
选项,可以只打印源代码。方便和mc/redefine
命令结合使用。
1 | bash复制代码$ jad --source-only demo.MathGame |
反编译指定的函数
$ jad demo.MathGame main
4.后台执行
arthas中的后台异步任务,使用了仿linux系统任务相关的命令。
1.使用&在后台执行任务
比如希望执行后台执行trace命令,那么调用下面命令
trace Test t &
这时命令在后台执行,可以在console中继续执行其他命令。
2.通过jobs查看任务
如果希望查看当前有哪些arthas任务在执行,可以执行jobs命令,执行结果如下
kill job-id
可以杀死指定任务
1 | bash复制代码$ jobs |
可以看到目前有一个后台任务在执行。
- job id是10, * 表示此job是当前session创建
- 状态是Stopped
- execution count是执行次数,从启动开始已经执行了19次
- timeout date是超时的时间,到这个时间,任务将会自动超时退出
任务转到前台执行fg <job-id>
可以把对应的任务转到前台继续执行。bg <job-id>
可以把对应的任务在后台继续执行
任务输出重定向
可通过>或者>>将任务输出结果输出到指定的文件中,可以和&一起使用,实现arthas命令的后台异步任务
$ trace Test t >> test.out &
今天的你多努力一点,明天的C位就是你!
一起学习成为 C位程序员。💋
微信公众号已开启,【C位程序员】,没关注的同学们记得关注哦!
本文转载自: 掘金