一 . 前言
Arthas 是一个 Java 诊断程序 , Arthas 官方 已经很详细的描述了命令的使用 , 如果想整体学习可以查看.
而这一篇文档记录了从最开始接触到 Arthas 到逐步深入过程中的一些记录和感悟 , 整理成文档以便处理.
使用场景
- 如果是一个可以复现的线上问题,而代码中又无法看出为什么会出现这样的问题
- 如果你先追踪线上的实际运行情况,在线上 debug 代码逻辑
- 如果你想直接介入线上代码的运行,并且对线上代码进行热更新
二. 入门使用
// 官方文档 @ https://arthas.aliyun.com/doc/quick-start.html// Step 1 :下载官方 JAR
wget https://arthas.aliyun.com/arthas-boot.jar// Step 2 : 启动 arthas
java -jar arthas-boot.jar// Step 3 : 选择对应的Java 进程 (PS : 启动后会显示所有的进程)// Step 4 : 输入 dashboard , 查看当前进程信息// 补充 : 其他主要的参数
- 通过thread命令来获取到MainClass : thread 1 | grep 'main('
- 通过 jad 反编译 : jad demo.MathGame
- 通过watch命令来查看返回值 : watch demo.MathGame primeFactors returnObj
三. 命令整理
四. 问题梳理
4.1 ClassLoader 在其中的作用
在很多命令中都涉及到 classLoader , 我们可以通过 classLoader 先获取到 hash 码 ,再查询具体的参数
// 查看 classLoader
classloader -l// 在查看对象的时候通过 classLoader 获取对象
getstatic -c 3d4eac69 ClassName methodName
mc -c 327a647b /tmp/Test.java >> 通过 ClassLoader 加载
总结
Arthas 的初始操作不多 ,更多的应该是基于实际问题的处理,后续文章会陆续往这个方向思考。
附录:常见使用方式
n.1 展示当前系统实时运行情况
通过运行 dashboard 命令即可查看当前系统的运行情况 , 有如下核心参数 :
n.2 Thread 进程获取函数信息
// 显示最忙的3个线程
> thread -n 2// 显示当前运行的线程
> thread
> thread all// 显示指定线程ID的线程
> thread 543// 指定时间间隔
> thread -n 3 -i 1000// 查看指定状态的线程
> thread --state WAITING// 查找当前阻塞的线程
thread -b
n.3 jad 反编译代码
// 反编译指定类路径
> jad com.mythymeleaf.demo.config.MyWebMvcConfig// 反编译只生成源码
> jad --source-only demo.MathGame// 反编译指定方法
> jad demo.MathGame main
n.4 JVM 查看
- LOADED-CLASS-COUNT : 加载的class数目
- COUNT: JVM 当前活跃的线程数
- DAEMON-COUNT: JVM 当前活跃的守护线程数
- PEAK-COUNT: 从 JVM 启动开始曾经活着的最大线程数
- STARTED-COUNT: 从 JVM 启动开始总共启动过的线程次数
- DEADLOCK-COUNT: JVM 当前死锁的线程数
- MAX-FILE-DESCRIPTOR-COUNT:JVM 进程最大可以打开的文件描述符数
- OPEN-FILE-DESCRIPTOR-COUNT:JVM 当前打开的文件描述符数
n.5 查看 JVM 已加载的类
SC 查看 JVM 中已经加载的类
// 模糊查找加载的类型
sc demo.*// 显示类的详细信息
sc -d com.mythymeleaf.demo.controller.DemoController// 输出类的成员变量信息
sc -d -f com.mythymeleaf.demo.controller.DemoController
SM 查询类的方法信息
// 查询类中的方法
sm java.lang.String// 查看方法的详细信息
sm -d java.lang.String toString
getstatic 获取类的静态信息
getstatic ClassName method
ognl 查看变量的值
ognl 就需要深度说说了 ,ognl 是一种强大的表达式语言 , 该语言最重要的是支持对对象方法的调用,同时支持类静态的方法调用和值访问 , ognl 的调用主要分为以下几类 :
- 直接调用静态函数 : 写法就是 @类@方法 ,同时可以把参数传进去
- 查看静态字段 : 同样 @类@字段名
- 集合操作 :
- 赋值操作 : 以 #value1=? 格式 , 创建对象等
@ https://cloud.tencent.com/developer/article/1554322
// ognl 原生语法
@java.lang.String@format('foo %s', 'bar')// 调用静态函数 (调用后会直接在服务上执行)
ognl '@java.lang.System@out.println("hello")'
ognl '@java.lang.Math@max(10, 20)'// 获取静态字段
ognl '@demo.MathGame@random'// 普通对象调用
ognl '#obj.method( 参数 )'// 赋值操作
ognl '#value="abv"'
ognl '#value1="abc", #value2="efg", {#value1, #value2}'// 对象创建
ognl 'new java.lang.String("hello world")'
ognl '(#user=new User(), #user.name="一灰灰Blog", #user.age=18, #user)'// 集合操作
ognl '"name" in {"name", "hello"}' >> 判断是否包含
n.6 方法执行情况监控 - Watch
Watch 的 表达式常量 :
- clazz : 对象的类
- method : 构造函数或方法
- params : 方法的参数数组
- params[0..n] : 参数数组的元素
- returnObj : method的返回对象
- throwExp : 方法的抛出异常
- isReturn : 返回结束的方法
- isThrow : 方法以引发异常结束
Watch 常用的语句
// 查看方法的执行情况 (会打印6次方法的调用)
watch com.test.points.clean.impl.UserServices processData -n 6// 打印指定输入参数的方法
watch com.test.points.clean.impl.UserServices processData 'params[0]=="Arthas"'
watch com.test.points.clean.impl.UserServices processData 'params[0].size==2'
watch com.test.points.clean.impl.UserServices processData 'params[1].username="antBlack"'// 指定打印参数
watch com.test.points.clean.impl.UserServices processData {params,returnObj}
watch com.test.points.clean.impl.UserServices processData {clazz,returnObj}
watch com.test.points.clean.impl.UserServices processData {method,returnObj}// 指定对象的遍历深度 (参数可能只会打一个引用 , 则需要通过遍历深度进行更深度的打印)
watch com.test.points.clean.impl.UserServices processData {params,returnObj} -x 2// 查看返回异常的请求方法
watch com.test.points.clean.impl.UserServices processData throwExp 'params[0].size==2'
n.6 方法调用监控 - Monitor
Monitor 是一种维度的监控 ,主要监控调用次数 , 成功次数 ,失败次数等
// 统计方法的调用情况
monitor com.test.points.clean.impl.UserServices processData -c 5// 通过参数获取监控
monitor -c 5 com.test.points.clean.impl.UserServices processData "params[0] <= 2"
n.7 查看方法的调用路径 Trace
// 打印方法执行时间
trace com.test.points.clean.impl.UserServices processData // 跳过 JDK 方法// 只打印调用时间超过 10 秒的方法
trace com.test.points.clean.impl.UserServices processData '#cost > 10'// 打印多个类路径
trace -E com.test.ClassA|org.test.ClassB method1|method2|method3// 跳过 JDK 方法
trace --skipJDKMethod false com.test.points.clean.impl.UserServices processData -n 2
n.8 查看方法调用时空隧道 tt
// 查看调用情况 @ https://arthas.aliyun.com/doc/tt.html#%E8%AE%B0%E5%BD%95%E8%B0%83%E7%94%A8
tt -t com.gang.web.demo.controller.BeanController testUser
(PS : 这个调用可以实时记录)// 通过表达式进行筛选
tt -s 'method.name=="search"'// 有服务端重调 (通过索引ID)
tt -i 1004 -p