目录
一、Arthas是什么
二、Arthas能解决哪些问题
三、Arthas安装启动
3.1 环境说明
3.2 下载地址
3.3 快速使用
四、Arthas基础命令
五、Arthas查看jvm相关数据
六、Arthas搬砖过程中的常用命令
6.0 查看已加载的类信息
6.1 查看已加载类中的方法信息
6.2 反编译指定已加载类的源码
6.3 监视一个时间段中指定方法的执行次数,成功次数,失败次数
6.4 输出当前方法被调用的调用路径链
6.5 输出方法路径上的每个节点上耗时, 定位因 RT 高导致的性能问题
6.7 观察:返回值、抛出异常、入参,通过编写OGNL 表达式进行对应变量的查看
七、生产环境运行了java程序,在线调试实战
一、Arthas是什么
阿里开源的Java诊断工具,它可以在运行时对Java应用程序进行动态诊断和调试
二、Arthas能解决哪些问题
当你遇到以下类似问题而束手无策时,
Arthas
可以帮助你解决
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到 JVM 的实时运行状态?
三、Arthas安装启动
3.1 环境说明
Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,
也支持浏览器直接访问对应的ip+端口,固定端口 8563
默认情况下,arthas 只 listen 127.0.0.1,所以如果想从远程连接,使用 --target-ip参数指定 listen 的 IP
3.2 下载地址
github:https://github.com/alibaba/arthas
官网:https://arthas.aliyun.com/
版本:Arthas-3.6.7
3.3 快速使用
启动 :java -jar arthas-boot.jar
命令执行后会展示进程列表,输入数字编号进入对应的进程
运行日志路径:/root/logs/arthas/arthas.log
退出 arthas
如果只是退出当前的连接,其他客户端不受影响,可以用 quit或者exit命令
目标进程上的 arthas 还会继续运行,端口保持开放,下次连接时执行java -jar arthas-boot.jar可以直接连接上
如果想完全退出 arthas,可以执行stop命令
四、Arthas基础命令
version - 输出当前目标 Java 进程所加载的 Arthas 版本号
base64 - base64 编码转换,和 linux 里的 base64 命令类似
cat - 打印文件内容,和 linux 里的 cat 命令类似
cls - 清空当前屏幕区域
echo - 打印参数,和 linux 里的 echo 命令类似
grep - 匹配查找,和 linux 里的 grep 命令类似
help - 查看命令帮助信息
history - 打印命令历史
keymap - Arthas 快捷键列表及自定义快捷键
pwd - 返回当前的工作目录,和 linux 命令类似
quit - 退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
reset - 重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
session - 查看当前会话的信息
stop - 关闭 Arthas 服务端,所有 Arthas 客户端全部退出
tee - 复制标准输入到标准输出和指定的文件,和 linux 里的 tee 命令类似
五、Arthas查看jvm相关数据
jvm 相关
- dashboard - 当前系统的实时数据面板
- heapdump - dump java heap, 类似 jmap 命令的 heap dump 功能
- jvm - 查看当前 JVM 的信息
- memory - 查看 JVM 的内存信息
- ognl - 执行 ognl 表达式
- perfcounter - 查看当前 JVM 的 Perf Counter 信息
- sysenv - 查看 JVM 的环境变量
- sysprop - 查看和修改 JVM 的系统属性
- thread - 查看当前 JVM 的线程堆栈信息
- vmoption - 查看和修改 JVM 里诊断相关的 option
命令案例操作: dashboard
列详解:
字段 | 说明 |
---|---|
id | Java 级别的线程 ID |
name | 线程名称 |
group | 线程组名称 |
proirity | 线程优先级,1 ~ 10 之间的数字,越大优先级越高 |
state | 线程的状态 |
cpu | 线程的 cpu 使用率 |
delta_time | 上次采样之后线程运行增量 CPU 时间,数据格式为秒 |
time | 线程运行总 CPU 时间,数据格式为 分:秒 |
interupted | 当前线程是否中断 |
daemon | 是否是 daemon 守护线程 |
字段 | 说明 |
---|---|
used | 当前使用了多少内存 |
total | 总共分配了多少内存 |
max | 最大使用了多少 |
usage | 使用比例 |
gc | 垃圾回收器 |
命令案例操作:thread
thread - 查看当前 JVM 的线程堆栈信息
-
--all :显示所有匹配的线程,默认就是第一页线程信息
-
-i:设置cpu统计时的采样间隔,单位为毫秒
thread -i 2000
-
[ id ]:查看指定ID的线程堆栈
thread 54
-
-n :查看CPU使用率最高的TopN个线程, 如果值为-1表示显示所有线程
thread -n 3
-
-b :展示阻塞线程
thread -b
-
--state : 根据线程状态筛选线程
thread --state TIMED_WAITING
- 状态类型:NEW, RUNNABLE, TIMED_WAITING, WAITING, BLOCKED,TERMINATED
六、Arthas搬砖过程中的常用命令
6.0 查看已加载的类信息
sc -d -f net.wnn.archwebproject.ProductController # -d 详情,-f 类属性输出
6.1 查看已加载类中的方法信息
sm -d net.wnn.archwebproject.ProductController
6.2 反编译指定已加载类的源码
jad - 反编译指定已加载类的源码
-
反编译整个类
jad net.wnn.archwebproject.ProductController
-
通过
--source-only
选项,可以只打印源代码
-
应用场景
- 查看某个类的业务逻辑,方法逻辑
- 查看本地修改的代码是否线上成功生效
反编译类的某个方法 jad net.wnn.archwebproject.ProductController query
6.3 监视一个时间段中指定方法的执行次数,成功次数,失败次数
-
monitor - 方法执行监控
- 非实时响应,需要对应的方法有被调用才行,所以需要触发web接口请求
- 监视一个时间段中指定方法的执行次数,成功次数,失败次数,耗时等这些信息
monitor -c 2 net.xdclass.archwebproject.ProductController query
触发web接口请求:
timestamp:方法执行时间戳
class : 方法执行所属类
method:执行的方法名称
total:执行次数 success:成功次数 fail:失败次数 avg-rt:执行耗时(毫秒) fail-rate:失败百分比
6.4 输出当前方法被调用的调用路径链
stack - 输出当前方法被调用的调用路径, 一个方法被执行的路径非常多,不知道这个方法是从那里被执行,就可以使用
stack net.wnn.archwebproject.ProductController test2
6.5 输出方法路径上的每个节点上耗时, 定位因 RT 高导致的性能问题
trace - 方法内部调用,输出方法路径上的每个节点上耗时, 定位因 RT 高导致的性能问题,每次只能跟踪一级方法的调用链路
ts
: 时间戳,表示日志记录的时间,该字段的值为2023年8月03日上午10点48分44秒。
thread_name
: 线程名称,表示当前执行该日志记录的线程名称,该字段的值为http-nio-8080-exec-10。id
: 线程ID,表示当前执行该日志记录的线程ID,该字段的值为20。is_daemon
: 是否为守护线程,该字段的值为true,表示该线程是守护线程。priority
: 线程优先级,该字段的值为5,表示该线程的优先级为5。TCCL
: 线程上下文类加载器,表示当前线程的上下文类加载器为TomcatEmbeddedWebappClassLoader。
拓展一:
默认情况下,trace不会包含jdk里的函数调用,如果希望trace jdk里的函数, 需要显式设置--skipJDKMethod false
trace --skipJDKMethod false net.wnn.archwebproject.ProductController find
拓展二:
根据调用耗时,过滤大于10ms的调用路径
trace net.wnn.archwebproject.ProductController find '#cost >10'
6.7 观察:返回值
、抛出异常
、入参
,通过编写OGNL 表达式进行对应变量的查看
watch - 方法执行数据观测, 观察:返回值
、抛出异常
、入参
,通过编写OGNL 表达式进行对应变量的查看
- 应用场景:查看方法调用栈,参数入参,返回值等调试
- 默认的 观察表达式,默认值是
{params, target, returnObj}
- -x 参数表示遍历深度,可以调整来打印具体的参数和结果内容,默认值是 1, 最大是4
watch net.wnn.archwebproject.ProductController * {params,returnObj} -x 4
七、生产环境运行了java程序,在线调试实战
背景:
生产环境运行了java程序,需要在线调试
不重启JVM程序,动态调整,打印参数或修改方法内部逻辑
使用到的相关命令:
- jad 把字节码文件反编译成源代码
- mc 在内存中把源代码编译成字节码文件
- redefine 把新生成的字节码文件在内存中执行
命令学习:
mc Memory Compiler/内存编译器,编译.java
文件生成.class
# 在内存中编译Hello.java为Hello.class
mc /root/Hello.java# 可以通过-d命令指定输出目录(输出对应的路径会有package路径)
mc -d /root/test /root/Hello.java
-
redefine
- 加载外部的.class文件,redefine到JVM里
- redefine后的原来的类不能恢复,redefine有可能失败(比如增加了新的field)
- 不允许新增加field/method
- 正在跑的函数,没有退出不能生效 比如main
实战操作:修改某个方法的接口返回值,增加多一个参数
步骤一、
#使用jad反编译 jad --source-only net.wnn.archwebproject.ProductController > /dev1/ProductController.java
步骤二、 修改代码
步骤三、
#使用mc内存中对新的代码编译
mc /dev1/ProductController.java -d /dev1
注意,mc 命令有可能失败。如果编译失败可以在本地编译好
.class
文件,再上传到服务器
编译四、redefine /dev1/ProductController.class
然后再请求下路径即可
Arthas官网链接:mc | arthas