带着8个问题5分钟教你学会Arthas诊断工具
ztj100 2025-07-02 00:18 17 浏览 0 评论
Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。
当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到JVM的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
- 怎样直接从JVM内查找某个类的实例?
这 8 个问题,Arthas 官方文档(https://arthas.aliyun.com/doc)中并没有给出答案或标准的解决方案。
坑爹啊
这不是管杀不管埋吗!!
准备
先给出我的测试代码
package com.admin.study;
import com.alibaba.fastjson.JSON;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class ArthasDemo {
public static void main(String[] args) {
String s = "[{\"name\":\"zhangsan\",\"age\":\"10\",\"telephone\":\"123456\",\"interests\":[\"sing\",\"dance\",\"rap\"]},\n" +
"{\"name\":\"lisi\",\"age\":\"20\",\"telephone\":\"123457\",\"interests\":[\"sing\",\"swim\"]},\n" +
"{\"name\":\"wangwu\",\"age\":\"30\",\"telephone\":\"123458\",\"interests\":[\"sing\",\"program\"]}]";
//模拟一遍遍的调用方法的过程
for (; ; ) {
System.out.println(new ArthasDemo().convert(s));
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private List<People> convert(String s) {
return JSON.parseArray(s, People.class);
}
@Getter
@Setter
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
private static class People {
/**
* 姓名
*/
String name;
/**
* 年龄
*/
String age;
/**
* 电话
*/
String telephone;
/**
* 兴趣列表
*/
List<String> interests;
}
}
以下是控制台正常打印的结果
/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/bin/java ...
[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]
[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]
下载并运行 Arthas
按照下图中的步骤,选择一个 Java 进程进行 attach。
下载并运行Arthas
访问 WebConsole
attach 成功后可以打开谷歌浏览器输入http://127.0.0.1:3658/ 打开 WebConsole
(吐槽一句 Mac OS 的 Safari 浏览器不支持)
使用 WebConsole 最方便的是你可以打开多个标签页同时操作
问题 1:这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
这个问题我经常在处理各种「依赖冲突」的时候遇到,有一些类的完全名称是一模一样,通过常规的办法无法解决类具体从哪个 jar 包加载。
别急,看我下面的解决办法。
- sc
通过 sc 命令 模糊查看当前 JVM 中是否加载了包含关键字的类,以及获取其完全名称。
注意使用 sc -d 命令,获取 classLoaderHash,这个值在后面需要用到。
sc -d *ArthasDemo*
sc-d命令
- classloader
通过 classloader 查看 class 文件来自哪个 jar 包
使用 cls 命令可以清空命令行,这个简单的命令官方文档居然找不到。。。
注意 classloader -c 后面的值填上面第一步中获取到的 Hash 值,class 文件路径使用'/'分割,且必须以.class 结尾。
[arthas@3633]$ classloader -c 18b4aac2 -r com/admin/study/ArthasDemo.class
file:/Users/admin/code/concurrentbook/target/classes/com/shockang/study/ArthasDemo.class
Affect(row-cnt:1) cost in 0 ms.
上面是显示 class 文件路径的,如果 class 文件来自 jar 包,可以显示 jar 包路径,例如官方文档给的例子:
$ classloader -c 1b6d3586 -r java/lang/String.class
jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/String.class
问题 2:我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
推荐使用 watch 和 tt 命令,非常好用。
这两个命令都是用来查看方法调用过程的,不同的是 watch 命令是调用一次打印一次方法的调用情况,而 tt 命令可以先生成一个不断增加的调用列表,然后指定其中某一项进行观测。
- 使用 watch 命令查看方法调用情况。我们要查看 ArthasDemo 这个类里面的 convert 方法调用情况。
watch命令
watch com.shockang.study.ArthasDemo convert "{params,target,returnObj}" -f -x 4
watch 后面跟上完全类名和方法名,以及一个 OGNL 的表达式,-f 表示不论正常返回还是异常返回都进行观察,-x 表示输出结果的属性遍历深度,默认为 1,
建议无脑写 4 就行,这是笔者经验来看最大的遍历深度,再大就不支持了
- 使用 tt 命令来观测方法调用情况,tt 命令可以查看「多次调用」并选择其中一个进行观测,但是如果输出结果是多层嵌套就没办法看了,而 watch 可以查看「多层嵌套」的结果。
使用 tt -t 记录下当前方法的每次调用环境现场
tt -t命令
tt -t com.shockang.study.ArthasDemo convert
TIMESTAMP表示方法调用发生的时间,COST 表示调用耗时(ms),IS-RET表示是否正常返回,IS-EXP 表示是否异常返回,OBJECT 表示对象的 HASH 值
对于具体一个时间片的信息而言,你可以通过 -i 参数后边跟着对应的 INDEX 编号查看到他的详细信息
tt-i命令
图中之所以可以打印兴趣列表,是调用了其 toString 方法,如果没有重写 java.lang.Object 类的 toString 方法,只会看到 hash 值。
- 如何判断代码是否已经提交?
通过 jad --source-only 可以查看源代码。
[arthas@3633]$ jad --source-only com.admin.study.ArthasDemo
/*
* Decompiled with CFR.
*/
package com.admin.study;
import com.alibaba.fastjson.JSON;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class ArthasDemo {
public static void main(String[] args) {
/*15*/ String s = "[{\"name\":\"zhangsan\",\"age\":\"10\",\"telephone\":\"123456\",\"interests\":[\"sing\",\"dance\",\"rap\"]},\n{\"name\":\"lisi\",\"age\":\"20
\",\"telephone\":\"123457\",\"interests\":[\"sing\",\"swim\"]},\n{\"name\":\"wangwu\",\"age\":\"30\",\"telephone\":\"123458\",\"interests\":[\"sing\",\"program\"]}]";
while (true) {
/*20*/ System.out.println(new ArthasDemo().convert(s));
try {
/*22*/ TimeUnit.SECONDS.sleep(10L);
/*25*/ continue;
}
catch (InterruptedException e) {
/*24*/ e.printStackTrace();
continue;
}
break;
}
}
private List<People> convert(String s) {
/*30*/ return JSON.parseArray(s, People.class);
}
private static class People {
private String name;
private String age;
private String telephone;
private List<String> interests;
private People() {
}
public String toString() {
return "ArthasDemo.People(name=" + this.getName() + ", age=" + this.getAge() + ", telephone=" + this.getTelephone() + ", interests=" + this.getIntere
sts() + ")";
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return this.age;
}
public String getTelephone() {
return this.telephone;
}
public List<String> getInterests() {
return this.interests;
}
public void setAge(String age) {
this.age = age;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public void setInterests(List<String> interests) {
this.interests = interests;
}
}
}
[arthas@3633]$
问题 3:遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
通过上面问题 2 的 watch 和 tt 命令可以查看方法调用情况。
此外,可以通过 redefine 命令「热替换」线上的代码,注意应用重启之后会失效,这在某些紧急情况下会有奇效。
比如说我们修改一下方法体里面的代码,加了一行日志打印:
private List<People> convert(String s) {
System.out.println(s);
return JSON.parseArray(s, People.class);
}
这时我们就可以将新代码编译后的 class 文件热替换正在运行的 ArthasDemo 的代码。
redefine命令
热替换 JVM 内存中(方法区)加载的类
从这张图可以明显的看出,明明源码中没有打印字符串 s 的逻辑,但是控制台还是打印了字符串,因为我们已经热替换了 JVM 内存中(方法区)加载的类。
问题 4:线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
这个问题没有完美的解决办法
参考一下问题 2 和问题 3的解决方案
推荐使用 tt 命令并将命令行返回结果输出到一个文件中,后续可以选择异常的一行记录使用 tt -i 命令进行深入的分析。
tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。
tee命令
tt -t com.admin.study.ArthasDemo convert | tee /Users/admin/Downloads/log
此外还可以使用 monitor 命令统计方法调用成功失败情况。
monitor命令
monitor -c 30 com.admin.study.ArthasDemo convert | tee /Users/admin/Downloads/log1
-c 后面接统计周期,默认值为120秒
问题 5:是否有一个全局视角来查看系统的运行状况?
使用 dashboard 命令可以查看当前系统的实时数据面板, 当运行在Ali-tomcat时,会显示当前tomcat的实时信息,如HTTP请求的qps, rt, 错误数, 线程池信息等等。
dashboard实时数据面板
从图中可以看到线程情况,内存使用情况,系统参数等。
问题 6:有什么办法可以监控到JVM的实时运行状态?
使用 jvm 命令可以查看 JVM 的实时运行状态。
JVM 的实时运行状态
问题 7:怎么快速定位应用的热点,生成火焰图?
profiler 命令支持生成应用热点的火焰图。本质上是通过不断的采样,然后把收集到的采样结果生成火焰图。
默认情况下,生成的是 cpu 的火焰图,即 event 是 cpu,可以用--event 参数来指定。注意不同系统支持的 event 不同
默认情况下,arthas使用3658端口,则可以打开:http://localhost:3658/arthas-output/ 查看到arthas-output目录下面的profiler结果:
profiler目录
选择一项点击
profiler结果图
问题 8:怎样直接从JVM内查找某个类的实例?
使用 vmtool 可以达成目的
这个功能是 Arthas 3.5.1 新增的。可以参考官方文档 https://arthas.aliyun.com/doc/vmtool.html#id1
$ vmtool --action getInstances --className java.lang.String --limit 10
@String[][
@String[com/taobao/arthas/core/shell/session/Session],
@String[com.taobao.arthas.core.shell.session.Session],
@String[com/taobao/arthas/core/shell/session/Session],
@String[com/taobao/arthas/core/shell/session/Session],
@String[com/taobao/arthas/core/shell/session/Session.class],
@String[com/taobao/arthas/core/shell/session/Session.class],
@String[com/taobao/arthas/core/shell/session/Session.class],
@String[com/],
@String[java/util/concurrent/ConcurrentHashMap$ValueIterator],
@String[java/util/concurrent/locks/LockSupport],
]
通过 --limit参数,可以限制返回值数量,避免获取超大数据时对JVM造成压力。默认值是10。
如果想精确的定位到具体的类实例,可以通过指定 classloader name 或者 classloader hash,如下所示:
vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext
vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext
获取 classloader hash 的方法请参考上面的问题 1
vmtool 还有个不错的功能,可以「强制进行GC」,这在某些生产环境内存紧张的情况下有奇效。
vmtool --action forceGc
相关推荐
- Linux集群自动化监控系统Zabbix集群搭建到实战
-
自动化监控系统...
- systemd是什么如何使用_systemd/system
-
systemd是什么如何使用简介Systemd是一个在现代Linux发行版中广泛使用的系统和服务管理器。它负责启动系统并管理系统中运行的服务和进程。使用管理服务systemd可以用来启动、停止、...
- Linux服务器日常巡检脚本分享_linux服务器监控脚本
-
Linux系统日常巡检脚本,巡检内容包含了,磁盘,...
- 7,MySQL管理员用户管理_mysql 管理员用户
-
一、首次设置密码1.初始化时设置(推荐)mysqld--initialize--user=mysql--datadir=/data/3306/data--basedir=/usr/local...
- Python数据库编程教程:第 1 章 数据库基础与 Python 连接入门
-
1.1数据库的核心概念在开始Python数据库编程之前,我们需要先理解几个核心概念。数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它就像一个电子化的文件柜,能让我们高效...
- Linux自定义开机自启动服务脚本_linux添加开机自启动脚本
-
设置WGCloud开机自动启动服务init.d目录下新建脚本在/etc/rc.d/init.d新建启动脚本wgcloudstart.sh,内容如下...
- linux系统启动流程和服务管理,带你进去系统的世界
-
Linux启动流程Rhel6启动过程:开机自检bios-->MBR引导-->GRUB菜单-->加载内核-->init进程初始化Rhel7启动过程:开机自检BIOS-->M...
- CentOS7系统如何修改主机名_centos更改主机名称
-
请关注本头条号,每天坚持更新原创干货技术文章。如需学习视频,请在微信搜索公众号“智传网优”直接开始自助视频学习1.前言本文将讲解CentOS7系统如何修改主机名。...
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
-
在Linux服务器管理中,SSH(SecureShell)是远程操作的核心工具。以下是SSH终端操作的常用命令和技巧,涵盖连接、文件操作、系统管理等场景:一、SSH连接服务器1.基本连接...
- Linux开机自启服务完全指南:3步搞定系统服务管理器配置
-
为什么需要配置开机自启?想象一下:电商服务器重启后,MySQL和Nginx没自动启动,整个网站瘫痪!这就是为什么开机自启是Linux运维的必备技能。自启服务能确保核心程序在系统启动时自动运行,避免人工...
- Kubernetes 高可用(HA)集群部署指南
-
Kubernetes高可用(HA)集群部署指南本指南涵盖从概念理解、架构选择,到kubeadm高可用部署、生产优化、监控备份和运维的全流程,适用于希望搭建稳定、生产级Kubernetes集群...
- Linux项目开发,你必须了解Systemd服务!
-
1.Systemd简介...
- Linux系统systemd服务管理工具使用技巧
-
简介:在Linux系统里,systemd就像是所有进程的“源头”,它可是系统中PID值为1的进程哟。systemd其实是一堆工具的组合,它的作用可不止是启动操作系统这么简单,像后台服务...
- Linux下NetworkManager和network的和平共处
-
简介我们在使用CentoOS系统时偶尔会遇到配置都正确但network启动不了的问题,这问题经常是由NetworkManager引起的,关闭NetworkManage并取消开机启动network就能正...
你 发表评论:
欢迎- 一周热门
-
-
MySQL中这14个小玩意,让人眼前一亮!
-
旗舰机新标杆 OPPO Find X2系列正式发布 售价5499元起
-
面试官:使用int类型做加减操作,是线程安全吗
-
C++编程知识:ToString()字符串转换你用正确了吗?
-
【Spring Boot】WebSocket 的 6 种集成方式
-
PyTorch 深度学习实战(26):多目标强化学习Multi-Objective RL
-
pytorch中的 scatter_()函数使用和详解
-
与 Java 17 相比,Java 21 究竟有多快?
-
基于TensorRT_LLM的大模型推理加速与OpenAI兼容服务优化
-
这一次,彻底搞懂Java并发包中的Atomic原子类
-
- 最近发表
-
- Linux集群自动化监控系统Zabbix集群搭建到实战
- systemd是什么如何使用_systemd/system
- Linux服务器日常巡检脚本分享_linux服务器监控脚本
- 7,MySQL管理员用户管理_mysql 管理员用户
- Python数据库编程教程:第 1 章 数据库基础与 Python 连接入门
- Linux自定义开机自启动服务脚本_linux添加开机自启动脚本
- linux系统启动流程和服务管理,带你进去系统的世界
- CentOS7系统如何修改主机名_centos更改主机名称
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
- Linux开机自启服务完全指南:3步搞定系统服务管理器配置
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)