系统稳定性—Java诊断工具Arthas使用
一、概述
Arthas是Alibaba开源的Java诊断工具,深受开发者喜爱。
当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
- 这个类从哪个
jar包加载的?为什么会报各种类相关的Exception? - 我改的代码为什么没有执行到?难道是我没
commit?分支搞错了? - 遇到问题无法在线上
debug,难道只能通过加日志再重新发布吗? - 线上遇到某个用户的数据处理有问题,但线上同样无法
debug,线下无法重现! - 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到
JVM的实时运行状态? - 怎么快速定位应用的热点,生成火焰图?
- 怎样直接从
JVM内查找某个类的实例?
Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的Tab自动补全功能,进一步方便进行问题的定位和诊断。
二、安装与使用
2.1 使用arthas-boot(推荐)
下载arthas-boot.jar,然后用java -jar的方式启动:
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
打印帮助信息:
java -jar arthas-boot.jar -h
- 如果下载速度比较慢,可以使用
aliyun的镜像:java -jar arthas-boot.jar --repo-mirror aliyun --use-http
2.2 使用as.sh
Arthas支持在Linux/Unix/Mac等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲回车执行即可:
curl -L https://arthas.aliyun.com/install.sh | sh
上述命令会下载启动脚本文件as.sh到当前目录,你可以放在任何地方或将其加入到$PATH中。
直接在shell下面执行./as.sh,就会进入交互界面。
也可以执行./as.sh -h来获取更多参数信息。
三、核心监视功能
3.1 monitor
监控指定类中方法的执行情况
用来监视一个时间段中指定方法的执行次数,成功次数,失败次数,耗时等这些信息
参数说明
方法拥有一个命名参数 [c:],意思是统计周期(cycle of output),拥有一个整型的参数值
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配 |
| method-pattern | 方法名表达式匹配 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
| [c:] | 统计周期,默认值为120秒 |
3.2 watch
方法执行数据观测,让你能方便的观察到指定方法的调用情况。
能观察到的范围为:返回值、抛出异常、入参,通过编写OGNL表达式进行对应变量的查看。
参数说明
watch的参数比较多,主要是因为它能在4个不同的场景观察对象
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配 |
| method-pattern | 方法名表达式匹配 |
| express | 观察表达式 |
| condition-express | 条件表达式 |
| [b] | 在方法调用之前观察before |
| [e] | 在方法异常之后观察 exception |
| [s] | 在方法返回之后观察 success |
| [f] | 在方法结束之后(正常返回和异常返回)观察 finish |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
| [x:] | 指定输出结果的属性遍历深度,默认为 1 |
3.3 trace
方法内部调用路径,并输出方法路径上的每个节点上耗时
trace命令能主动搜索class-pattern/method-pattern对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。
参数说明
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配 |
| method-pattern | 方法名表达式匹配 |
| condition-express | 条件表达式 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
[n:] |
命令执行次数 |
#cost |
方法执行耗时 |
这里重点要说明的是观察表达式,观察表达式的构成主要由ognl表达式组成,所以你可以这样写"{params,returnObj}",只要是一个合法的ognl表达式,都能被正常支持。
观察的维度也比较多,主要体现在参数advice的数据结构上。Advice参数最主要是封装了通知节点的所有信息。
3.4 stack
输出当前方法被调用的调用路径
很多时候我们都知道一个方法被执行,但这个方法被执行的路径非常多,或者你根本就不知道这个方法是从那里被执行了,此时你需要的是stack命令。
参数说明
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配 |
| method-pattern | 方法名表达式匹配 |
| condition-express | 条件表达式 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
[n:] |
执行次数限制 |
这里重点要说明的是观察表达式,观察表达式的构成主要由ognl表达式组成,所以你可以这样写"{params,returnObj}",只要是一个合法的ognl表达式,都能被正常支持。
观察的维度也比较多,主要体现在参数advice的数据结构上。Advice参数最主要是封装了通知节点的所有信息。
使用例子
stack demo.MathGame primeFactors
据条件表达式来过滤
stack demo.MathGame primeFactors 'params[0]<0' -n 2
据执行时间来过滤
stack demo.MathGame primeFactors '#cost>5'
3.5 tt(Time Tunnel)
方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
watch虽然很方便和灵活,但需要提前想清楚观察表达式的拼写,这对排查问题而言要求太高,因为很多时候我们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜测。
这个时候如果能记录下当时方法调用的所有入参和返回值、抛出的异常会对整个问题的思考与判断非常有帮助。
于是乎,TimeTunnel命令就诞生了。
作用:记录指定方法每次调用的入参和返回值,并后期还可以对这些信息进行观测
参数说明
| tt的参数 | 说明 |
|---|---|
| -t | 记录某个方法在一个时间段中的调用 |
| -l | 显示所有已经记录的列表 |
| -n 次数 | 只记录多少次 |
| -s 表达式 | 搜索表达式 |
| -i 索引号 | 查看指定索引号的详细调用信息 |
| -p | 重新调用:指定的索引号时间碎片 |
四、项目中使用
4.1 jad
反编译指定已加载类的源码
jad命令将JVM中实际运行的class的byte code反编译成java代码,便于你理解业务逻辑;
- 在
Arthas Console上,反编译出来的源码是带语法高亮的,阅读更方便 - 当然,反编译出来的
java代码可能会存在语法错误,但不影响你进行阅读理解
4.1.1 参数说明
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配 |
[c:] |
类所属 ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
4.1.2 使用参考
- 反编译
java.lang.String:jad java.lang.String - 反编译时只显示源代码:默认情况下,反编译结果里会带有
ClassLoader信息,通过--source-only
选项,可以只打印源代码。方便和mc /retransform命令结合使用。jad --source-only demo.MathGame - 反编译指定的函数:
jad demo.MathGame main - 反编译时不显示行号:
--lineNumber
参数默认值为true,显示指定为false则不打印行号。jad demo.MathGame main --lineNumber false - 反编译时指定
ClassLoader
当有多个
ClassLoader都加载了这个类时,jad命令会输出对应ClassLoader</span>实例的<span class="pre">hashcode,然后你只需要重新执行jad命令,并使用参数-c <hashcode>就可以反编译指定ClassLoader加载的那个类了;
jad org.apache.log4j.Logger对于只有唯一实例的ClassLoader还可以通过--classLoaderClass指定class name,使用起来更加方便:
--classLoaderClass的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而-c <hashcode>是动态变化的。
五、常用命令
5.1 help
查看帮助信息
5.2 version
查看
Arthas版本信息
5.3 cat
显示文件下,文本内容。
如果没有写路径,则显示当前目录下的文件
5.4 grep
在文件中搜索关键字
参数
| 参数列表 | 作用 |
|---|---|
| -n | 显示行号 |
| -i | 忽略大小写查找 |
| -m 行数 | 最大显示行数,要与查询字符串一起使用 |
| -e “正则表达式” | 使用正则表达式查找 |
5.5 pwd
查看当前目录
5.6 session
显示当前会话信息
5.7 reset
重置增强类,将被
Arthas增强过的类全部还原,Arthas服务端关闭时会重置所有增强过的类。
5.8 history
显示历史命令
5.9 quit
退出当前
Arthas客户端,其它客户端不受影响。
用exit或者quit命令可以退出Arthas。
退出Arthas之后,还可以再次用java -jar arthas-boot.jar来连接。
exit/quit命令只是退出当前session,arthas server还在目标进程中运行。想完全退出Arthas,可以执行stop命令。
5.10 stop
关闭
Arthas服务端,所有Arthas服务端全部退出。
5.11 keymap
显示当前快捷键
六、JVM命令
6.1 dashboard
当前系统的实时数据面板,按
ctrl+c退出。
当运行在Ali-tomcat时,会显示当前tomcat的实时信息,如HTTP请求的qps,rt,错误数,线程池信息等等。
参数说明
| 参数名称 | 参数说明 |
|---|---|
| [i:] | 刷新实时数据的时间间隔 (ms),默认5000ms |
| [n:] | 刷新实时数据的次数 |
6.2 thread
查看当前线程信息,查看线程的堆栈
参数说明
| 参数名称 | 参数说明 |
|---|---|
| id | 线程id |
| [n:] | 指定最忙的前N个线程并打印堆栈 |
| [b] | 找出当前阻塞其他线程的线程 |
[i value] |
指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200 |
| [--all] | 显示所有匹配的线程 |
cpu使用率是如何统计出来的?
这里的cpu使用率与linux命令top -H -p <pid> 的线程%CPU类似,一段采样间隔时间内,当前JVM里各个线程的增量cpu
时间与采样间隔时间的比例。
6.2.1 工作原理
- 首先第一次采样,获取所有线程的
CPU时间(调用的是java.lang.management.ThreadMXBean#getThreadCpuTime()
及sun.management.HotspotThreadMBean.getInternalThreadCpuTimes()接口) - 然后睡眠等待一个间隔时间(默认为
200ms,可以通过-i指定间隔时间) - 再次第二次采样,获取所有线程的
CPU时间,对比两次采样数据,计算出每个线程的增量CPU时间 - 线程
CPU使用率 = 线程增量CPU时间 / 采样间隔时间 * 100%
注意:这个统计也会产生一定的开销(
JDK这个接口本身开销比较大),因此会看到as
的线程占用一定的百分比,为了降低统计自身的开销带来的影响,可以把采样间隔拉长一些,比如5000毫秒。
另外一种查看
Java进程的线程cpu
使用率方法:可以使用show-busy-java-threads
这个脚本
6.2.2 使用参考
thread -n,支持一键展示当前最忙的前N个线程并打印堆栈:thread -n 3
- 没有线程ID,包含
[Internal]表示为JVM内部线程。 cpuUsage为采样间隔时间内线程的CPU使用率,与[dashboard]命令的数据一致。deltaTime为采样间隔时间内线程的增量CPU时间,小于1ms时被取整显示为0ms。time线程运行总CPU时间。
注意:线程栈为第二采样结束时获取,不能表明采样间隔时间内该线程都是在处理相同的任务。建议间隔时间不要太长,可能间隔时间越大越不准确。可以根据具体情况尝试指定不同的间隔时间,观察输出结果。
当没有参数时,显示第一页线程的信息,默认按照CPU增量时间降序排列,只显示第一页数据。
thread –all,显示所有匹配的线程,显示所有匹配线程信息,有时需要获取全部JVM的线程数据进行分析。
thread id,显示指定线程的运行堆栈,如thread 1
thread
-b,找出当前阻塞其他线程的线程,有时候我们发现应用卡住了,通常是由于某个线程拿住了某个锁,并且其他线程都在等待这把锁造成的。为了排查这类问题,arthas
提供了thread -b,一键找出那个罪魁祸首。thread b
注意,目前只支持找出synchronized关键字阻塞住的线程,如果是
java.util.concurrent.Lock,目前还不支持。
thread -i,指定采样时间间隔
thread -i 1000:统计最近1000ms内的线程CPU时间。thread -n 3 -i 1000:列出1000ms内最忙的3个线程栈
thread –state,查看指定状态的线程:thread --state WAITING
6.3 jvm
查看当前JVM信息
6.4 sysprop
查看当前JVM的系统属性(
System Property)
6.5 sysenv
查看当前JVM的环境属性(
System Environment Variables)
6.6 vmtool
vmtool利用JVMTI接口,实现查询内存对象,强制GC等功能。
6.7 mc
Memory Compiler/内存编译器,编译
.java文件生成.class。
mc /tmp/Test.java
可以通过-c参数指定classloader:
mc -c 327a647b /tmp/Test.java
也可以通过<span class="pre">--classLoaderClass</span>参数指定ClassLoader:
mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader
可以通过-d命令指定输出目录:
mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java
编译生成.class文件之后,可以结合retransform命令实现热更新代码。
6.8 retransform
加载外部的
.class文件,retransform热更新jvm`已加载的类。
retransform /tmp/Test.class
retransform -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class
6.9 sc
查看
JVM已加载的类信息
“Search-Class”的简写,这个命令能搜索出所有已经加载到JVM中的Class信息,这个命令支持的参数有[d]、[E]、[f]和[x:]。
参数说明
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配 |
| method-pattern | 方法名表达式匹配 |
| [d] | 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。如果一个类被多个ClassLoader所加载,则会出现多次 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
| [f] | 输出当前类的成员变量信息(需要配合参数-d一起使用) |
| [x:] | 指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出 |
[c:] |
指定class的 ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
[n:] |
具有详细信息的匹配类的最大数量(默认为100) |

浙公网安备 33010602011771号