Java诊断工具 | Arthas
1. Arthas是什么
Arthas官网
Arthas 是Alibaba开源的Java诊断工具
2. Arthas可以解决的问题
2-1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 使用
sc className -d
可以查看类的详细信息,包括类路径 - 使用
trace ClassName functionName
可以查看方法的调用链,可以看到其中调用方法抛出的Exception
2-2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 使用
sc newClassName
查看新添加的类是否存在 - 使用
sm className.newFunction -d
查看新添加的方法是否存在 - 使用
jad classname
反向编译出java文件,查看新添加的改动是否存在
2-3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 使用
watch ClassName functionName '{loader,clazz,method,target,params,returnObj,throwExp}' 'condition'
可以查看方法的调用详情,包括方法的调用者信息,入参,返回值,异常,运行时间等. - 使用
tt -t ClassName functionName -n 5
可以记录并显示方法的前五次调用信息,而且可以通过tt -i index_no -p
的方式重现方法调用.
2-4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
2-5. 是否有一个全局视角来查看系统的运行状况?
dashboard
可以提供一个系统的实时数据面板
2-6. 有什么办法可以监控到JVM的实时运行状态?
jvm
可以查看jvm的运行数据
2-7. 怎么快速定位应用的热点,生成火焰图?
- 启动profiler
profiler start
- 获取已采集的sample的数量
profiler getSamples
- 查看profiler状态
profiler status
- 停止profiler
profiler stop
,这时会输出火焰图的位置,打开即可查看
3. Arthas的安装和使用
下载arthas-boot.jar,然后用java -jar的方式启动:
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar
- idea上可以下载插件arthas idea
4. jvm相关命令
4-1. dashboard
当前系统的实时数据面板,ctrl + c
中断

dashboard
4-2. thread
查看jvm线程堆栈信息,thread -b
可以查看死锁
thread
显示所有线程的概览thread id
显示指定线程ID的堆栈信息thread -n 3
查看当前最忙的前三个线程thread -b
找出现在阻塞其他线程的线程thread -i 5000
指定采样间隔,这个是采集5秒内的数据.会在5秒后返回统计信息thread -state WAITING
指定显示某种状态的线程
4-3. jvm
查看jvm整体概览,可以查看运行时间
jvm
显示jvm的数据汇总,具体内容分为以下几块runtime
包括jvm开始时间,启动参数,class_path等class-loading
已加载类的数量,总共加载类数量,已卸载类的数量garbage-collectors
显示使用的垃圾收集器及垃圾收集次数memory
堆内存空间使用情况thread
线程总数,守护线程数,死锁数量
4-4. sysprop
查看系统的属性
4-5. sysenv
查看jvm的环境变量,包括jvm运行的main方法类
4-6. getstatic
查看类的静态属性
4-7. ognl
调用静态方法
ognl '@java.lang.System@out.println("hello")'
会在程序的控制台输出hello
5. class/classloader相关
5-1. sc
查看jvm已加载的类的信息search class,支持*
号匹配
- 可用参数
-d
列出类的详细信息,包括类名,类所在的位置,类的ClassLoader-f
需要与-d同时使用,显示类的成员变量
5-2. sm
查看类的方法信息search method
- 可用参数
-d
展示详细方法描述(方法所在类,方法名,修饰符,入参,返回值)
5-3. jad [className]
反编译Class
5-4. mc
内存编绎器memory compile,把.java文件编译为.class文件
mc /temp/Test.java
- 可通过-c指定类加载器
mc -c 327a647b /tmp/Test.java
- 可通过-d指定输出目录
mc -d /tmp/output tmp/ClassA.java tmp.ClassB.java
- 可以配合
redefine
命令实现热更新代码
5-5. redefine
加载外部的.class文件,redefine jvm已加载的类
redefine /tmp/Test.class
redefine -c 327a647b /tmp/Test.class /tmp/Test$Inner.class
- redefine的限制
- 不允许新增field和method
- 正在跑的函数,没有退出不能生效,因为没退出的话,一直在栈中,代码无法重新加载,其他方法能够出栈重新再入栈后就是已经修改后的代码了.
5-6. dump
dump已加载类的byte code到特定目录
dump java.lang.String
将String的class文件dump到指定位置,会显示具体位置在哪里
5-7. classloader
查看所有加载的类信息,classloader信息
classloader
展示出所有已加载的类classloader -l
展示每个classloader加载了多少类classloader -t
展示类加载器的继承树
6. monitor/watch/trace相关
6-1. monitor
方法执行监控
monitor className functionName
监控className.functionName被调用的情况,默认120秒统计一次,可以使用-c 5
设置5秒统计一次- 统计信息包括时间戳,类名,方法名,这段时间内方法被调用的总次数,调用成功的次数,失败的次数,平均执行时间,失败率
6-2. watch
方法执行数据观测,包括入参,返回值,异常等
- watch的参数说明
class-pattern
类名表达式匹配method-pattern
方法名的表达式匹配express
观察表达式,ognl的格式- 可观察的内容有:
- lader:本次调用类所在的ClassLoader
- clazz:调用方法在哪个类中申明的
- method:显示方法在哪个类中被调用的
- target:本次调用类的实例
- params:本次调用参数列表,可使用下标获取某个位置的参数
params[0]
,如果参数为空则是个空数组 - returnObj:本次调用返回对象,方法正常返回的值,如果无返回值,则为null
- throwExp:本次调用抛出的异常,被捕获处理后的异常不算
- isThrow:是否抛出异常
- isReturn:是否正常返回
- isBefore
- 可观察的内容有:
condition-express
条件表达式-b
在方法调用之前观察-e
在方法异常之后观察-s
在方法返回之后观察-f
在方法结束之后观察,默认是这个-x
指定输出结果属性的遍历深度,默认为1
- 示例:
- 查看方法的参数和返回值:
watch com.MyClass myTest '{params,returnObj}'
- 按照条件过滤:
watch com.MyClass myTest '{params,target}' 'params[0]<0'
- 按照调用耗时(ms)过滤:
watch com.MyClass myTest '{params}' '#cost>200'
- 查看方法的参数和返回值:
6-3. trace
方法内部调用路径,并输出方法路径上的每个节点上耗时
trace com.MyClas myFun
trace com.MyClass myFun '#cost > 10'
根据调用耗时过滤
6-4. stack
输出当前方法被调用的调用路径
- 查看某个方法是从哪里被执行的.
6-5. tt
方法执行时数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同时间下的调用进行观测
tt -t MyClass myFun -n 3
记录下myFun的下三次调用信息,如果myFun有多个重载,它们的调用都被算入进来了,可以使用过滤条件过滤下.tt -t MyClass myFun 'params.length==1' 'params[0] instanceof Integer' 'isThrow'
记录myFun的调用信息,其中参数只有一个Integer类型的,且只有抛出异常了才记录tt -l
查看保存下来的调用信息tt -s 'method.name==myFun'
筛选方法名为myFun的记录tt -i 1000
查看指定index为1000的调用详情,其中1000是记录的编号tt -i 1000 -p
重新调用一次index为1000的这个方法- 注意,重新调用的时候ThreadLocal信息丢失
- tt保存的只是当时环境对象的引用,如果方法中改变了对象的内容,tt就无法看到当时最准确的值