Arthas入门手册
一份简单的Arthas入门手册
Arthas入门手册
介绍
Arthas
是一款开源的Java
诊断工具
Arthas
拥有实时反编译, 修改内存, 查询类加载器, 监控特定类或方法等功能, 能有效检查并解决生产环境中的各类问题
本文将介绍工具的下载运行与一些常用的指令使用方法, 更多详细内容还行查看官方文档: Arthas 用户文档
本文环境
系统: CentOS 7 虚拟机
JDK: OracleJDK1.8.0
Arthas: 3.5
下载 & 运行
下载
Arthas
下载地址: https://arthas.aliyun.com/arthas-boot.jar
wget
:1
wget https://arthas.aliyun.com/arthas-boot.jar
运行
Arthas
1
java -jar arthas-boot.jar
若直接运行如上命令, 可能产生如下第二行信息, 这只是说明当前没有其他正在运行的的
Java
进程, 没有可诊断对象但是若有其他
Java
程序正在运行, 却依然产生此信息, 则说明JDK
安装版本不满足要求. 可能安装的是OpenJDK
, 需要重新安装OracleJDK
1
2
3
4# java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.5.3
[INFO] Can not find java process. Try to run `jps` command lists the instrumented Java HotSpot VMs on the target system.
Please select an available pid.(可选)下载运行测试程序
官方提供了一个用于测试的
SpringBoot
程序, 可以运行此程序后再运行Arthas
wget
:1
wget https://github.com/hengyunabc/spring-boot-inside/raw/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar
运行:
1
java -jar demo-arthas-spring-boot.jar
也可以运行其他的
Java
程序用于诊断, 下文的一些诊断内容基于此程序重新运行
Arthas
重新运行
arthas-boot.jar
后产生类似如下内容:1
2
3
4# java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.5.3
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 1312 demo-arthas-spring-boot.jar选择进程
输入
1
并回车, 此操作是选择运行中demo-arthas-spring-boot.jar
进程作为诊断对象, 产生如下内容:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24# java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.5.3
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 2135 demo-arthas-spring-boot.jar
1
[INFO] arthas home: /root/.arthas/lib/3.5.3/arthas
[INFO] Try to attach process 2135
[INFO] Attach process 2135 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version 3.5.3
main_class
pid 2135
time 2021-08-26 09:17:45
[arthas@2135]$此时进入了
Arthas
工具, 显示ARTHAS
的图标, 且输入行首显示为[arthas@xxxx]$
常用命令
退出Arthas
exit
| quit
quit
或exit
1 | $ quit |
如此方式退出时, 并没有关闭Arthas
工具的进程
重新连接Arthas
只需重新输入运行命令
关闭Arthas
stop
1 | $ stop |
查看指令帮助
查看指令与介绍help
显示内容较多, 此处截取部分展示
1 | $ help |
查看单条指令详细信息-h
每条指令也都可添加参数-h
来获取详细信息, 以dashboard
指令为例:
1 | $ dashboard -h |
查看系统实时数据面板dashboard
dashboard
会循环显示系统实时数据
可以通过按键Q
或Ctrl + C
退出循环
显示内容较多, 此处截取部分展示
1 | $ dashboard |
查看线程信息thread
thread
会显示当前的线程信息, 可以检查线程状态, CPU
使用率等
显示内容较多, 此处截取部分展示
1 | $ thread |
可以通过参数-b
来查看阻塞的线程, 参数-n x
来查看最占用cpu
的前x
个线程
查看JVM已加载的类信息sc
查看JVM
中UserController
类的信息
1 | $ sc com.example.demo.arthas.user.UserController -d |
查看已加载类的方法信息sm
查看JVM
中UserController
中的方法信息
1 | $ sm com.example.demo.arthas.user.UserController -d |
或者指定查看的方法为findUserById
1 | $ sm com.example.demo.arthas.user.UserController findUserById -d |
反编译jad
反编译UserController
类
1 | $ jad com.example.demo.arthas.user.UserController |
添加参数--source-only
可以隐藏ClassLoader
与Location
信息
可以使用>
来输出到文件中
1 | $ jad com.example.demo.arthas.user.UserController > /tmp/UserController.java |
也可以反编译特定的函数
1 | $ jad com.example.demo.arthas.user.UserController findUserById --source-only --lineNumber false |
监控函数的返回值watch
监控UserController
的findUerById
方法的返回值
可以通过按键Q
或Ctrl + C
退出监控
1 | $ watch com.example.demo.arthas.user.UserController findUserById -x 2 |
这里使用参数-x
指定了展开层数为2, 所以才能看到返回的User
的具体内容
可以指定参数-e
, 只在发生异常时进行显示
监控节点耗时trace
监控UserController
的findUerById
方法调用耗时, 每个方法的耗时显示在行最前端
1 | $ trace com.example.demo.arthas.user.UserController findUserById |
可以使用参数--skipJDKMethod false
来监控JDK
函数耗时
使用案例
热更新代码
目前, 请求http://localhost/user/0
会产生java.lang.IllegalArgumentException
错误
1 | # curl http://localhost/user/0 |
原因在UserController.findByUserId
函数内, 现在通过热更新代码来解决这一错误
通过
jad
命令将UserController
类反编译为java
文件, 并保存至本地1
$ jad --source-only com.example.demo.arthas.user.UserController --lineNumber false > /tmp/UserController.java
使用
vi
或vim
编辑文件, 编辑后内容如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package com.example.demo.arthas.user;
import com.example.demo.arthas.user.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
public User findUserById( Integer id){
logger.info("id: {}", (Object)id);
if (id != null && id < 1) {
return new User(id, "name" + id);
// throw new IllegalArgumentException("id < 1");
}
return new User(id, "name" + id);
}
}通过
sc
命令查找到UserController
类的类加载器的hash
值1
2$ sc -d *UserController | grep classLoaderHash
classLoaderHash 5674cd4d*
为通配符, 类似模糊查找grep
为结果筛选, 此处只需要classLoaderHash
的值, 本次的值为5674cd4d通过
mc
命令将UserController.java
文件编译1
2
3
4$ mc -c 5674cd4d /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 466 ms.此处需要指定类的加载器, 已经通过第四步获取到
-d
参数指定编译文件的输出路径, 也存放在/tmp
中通过
redefine
命令重新加载编译后的文件1
2
3$ redefine /tmp/com/example/demo/arthas/user/UserController.class
redefine success, size: 1, classes:
com.example.demo.arthas.user.UserController
此时请求http://localhost/user/0
则正常返回数据
1 | # curl http://localhost/user/0 |
线程死锁诊断
上述例子中不存在线程死锁的情况, 故使用以下例子来进行测试
1 | package io.tomoto.controller; |
在请求该地址之前, 使用thread -b
命令, 提示没有阻塞的线程
1 | $ thread -b |
在请求后, 再次使用thread -b
, 提示存在阻塞的线程, 并指出代码位置
1 | $ thread -b |
之后就可以修改代码重新打包部署或者通过热更新代码来解决了
诊断Docker
容器运行的Java
程序
进入容器, 安装运行Arthas
1 | docker exec -it ${containerId} /bin/bash -c "wget https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar" |
可能遇到的问题
wget
失败原因: 无法
wget
到arthas-boot.jar
文件, 错误信息为wget: can't execute 'ssl_helper': No such file or directory
则是因为容器内置的wget
不支持https
解决方案: 需要从其他的
http
地址获取该文件, 或者在构建镜像时在Dockerfile
中添加该文件1
ADD arthas-boot.jar arthas-boot.jar
无法启动
Arthas
原因: 若无法运行
Arthas
, 可能是因为容器内安装的是JRE
而非JDK
, 或安装的是低版本的OpenJDK
, 缺少必要工具, 上文也有提到过此原因解决方案: 在
Dockerfile
中添加如下内容并重新构建镜像1
FROM java:8-alpine
Arthas
启动失败原因: 镜像启动时若直接运行
Java
进程,pid
为1
, 会导致一些功能无法使用, 这是docker
自身的问题解决方案: 若
docker
版本大于等于1.13
, 则可以在启动镜像时添加参数--init
来避免Java
进程pid
为1
1
docker run --init -dp 80:80 demo