Arthas工具使用详解:Java诊断利器实战指南

本文最后更新于 1 分钟前,文中所描述的信息可能已发生改变。

Arthas(阿尔萨斯)是阿里巴巴开源的一款Java应用诊断利器,它能够帮助开发人员直接在线上环境定位问题,无需修改代码或重启应用。本文将全面介绍Arthas的核心功能和实际应用场景,帮助开发者掌握这一强大的诊断工具。

Arthas简介

什么是Arthas

Arthas是一款开源的Java应用诊断工具,由阿里巴巴于2018年9月开源。作为一个Java应用诊断利器,它允许开发者在不重启、不修改代码的情况下,实时诊断线上应用的各种问题:

  • 实时查看JVM运行状态
  • 监控方法执行情况
  • 查看类加载信息
  • 查看调用堆栈
  • 热更新Java代码
  • 线程分析与诊断

Arthas基于Greys工具进行了重构和增强,采用命令行交互模式,提供了丰富的命令用于问题排查和性能调优。

为什么需要Arthas

在Java应用线上运行时,我们可能会遇到各种问题:

  • 应用卡顿,但无法确定是哪个方法耗时长
  • 内存泄漏,但不知道是哪些对象占用内存
  • 线程死锁或线程池满,但无法直接查看线程状态
  • 方法调用异常,但无法确定参数值
  • 代码逻辑有bug,但修复后需要重启应用

传统的解决方案通常是添加日志、远程调试或重启应用,这些方法要么侵入性强,要么会影响线上服务。Arthas的出现很好地解决了这些问题,让线上问题排查变得优雅和高效。

安装与启动

安装Arthas

Arthas提供了多种安装方式,最常用的是通过下载安装包安装:

bash
# 下载Arthas安装包
curl -O https://arthas.aliyun.com/arthas-boot.jar

# 或使用wget
wget https://arthas.aliyun.com/arthas-boot.jar

其他安装方式:

  1. 通过Maven仓库下载
xml
<dependency>
    <groupId>com.taobao.arthas</groupId>
    <artifactId>arthas-packaging</artifactId>
    <version>最新版本</version>
    <type>tar.gz</type>
</dependency>
  1. 通过Docker使用
bash
docker run --rm -it --name arthas-demo arthas/arthas-demo

启动Arthas

下载完成后,可以通过以下命令启动Arthas:

bash
# 启动Arthas
java -jar arthas-boot.jar

# 启动后会列出所有Java进程
# 输入应用对应的序号进行连接

启动后,Arthas会列出当前系统中所有的Java进程:

[INFO] arthas-boot version: 3.5.5
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 12345 com.example.MyApplication
  [2]: 23456 org.apache.catalina.startup.Bootstrap

输入对应的序号并回车,即可连接到指定的Java进程。

远程连接

Arthas支持远程连接模式,可以在一台机器上启动Arthas,然后在另一台机器上连接:

bash
# 在服务器上启动Arthas并开启远程连接
java -jar arthas-boot.jar --target-ip 0.0.0.0 --telnet-port 9998 --http-port 8563

# 在另一台机器上通过telnet连接
telnet 服务器IP 9998

# 或者通过浏览器访问Web控制台
http://服务器IP:8563

Arthas核心功能详解

基础命令

先来了解一些基础命令,帮助我们熟悉Arthas环境:

  1. help:查看命令帮助信息
help
  1. cls:清空屏幕
cls
  1. session:查看当前会话信息
session
  1. reset:重置增强类,清除所有的增强
reset
  1. version:查看Arthas版本号
version
  1. quit/exit:退出Arthas客户端
quit
  1. shutdown:关闭Arthas服务端
shutdown

JVM相关命令

dashboard - 系统实时数据面板

dashboard命令可以显示当前系统的实时数据面板,包括线程、内存、GC等信息:

dashboard

输出示例:

ID   NAME                           GROUP                 STATE      %CPU      DAEMON
-1   VM Periodic Task Thread        -                     RUNNABLE   0.0       true
1    main                           main                  RUNNABLE   0.0       false
2    Reference Handler              system                WAITING    0.0       true
3    Finalizer                      system                WAITING    0.0       true
...

Memory                    used        total      max        usage
heap                      32M         256M       4G         1.56%
ps_eden_space             14M         64M        1G         21.88%
ps_survivor_space         4M          16M        16M        25.00%
ps_old_gen                12M         176M       3G         0.59%
...

GC                                    count       time
MarkSweepCompact                      0           0
Copy                                  0           0
...

jvm - JVM信息

jvm命令可以查看当前JVM的详细信息:

jvm

输出包括:

  • Java版本
  • JVM参数
  • ClassLoader数量
  • 线程数量
  • 内存使用情况
  • 操作系统信息等

thread - 线程相关信息

thread命令用于查看当前JVM的线程堆栈信息:

# 显示所有线程信息
thread

# 显示指定线程的堆栈
thread 线程ID

# 查找占用CPU最高的前3个线程
thread -n 3

# 查看死锁线程
thread -b

# 查看指定状态的线程
thread -state BLOCKED

memory - 内存信息

memory命令用于查看JVM内存信息:

memory

sysprop - 系统属性

sysprop命令用于查看或修改系统属性:

# 查看所有系统属性
sysprop

# 查看指定系统属性
sysprop java.version

# 修改系统属性
sysprop user.timezone Asia/Shanghai

sysenv - 环境变量

sysenv命令用于查看系统环境变量:

# 查看所有环境变量
sysenv

# 查看指定环境变量
sysenv PATH

vmoption - 虚拟机选项

vmoption命令用于查看和修改JVM参数:

# 查看所有虚拟机参数
vmoption

# 查看指定参数
vmoption PrintGC

# 修改参数
vmoption PrintGC true

heapdump - 堆转储

heapdump命令可以生成堆转储文件,用于分析内存问题:

# 生成堆转储文件
heapdump /tmp/dump.hprof

类和方法相关命令

sc - 查找类

sc (Search Class) 命令用于查找加载的类:

# 查找指定名称的类
sc com.example.MyClass

# 使用正则表达式查找类
sc com.example.*Service

# 查看类的详细信息
sc -d com.example.MyClass

sm - 查找方法

sm (Search Method) 命令用于查找类中的方法:

# 查找类中的方法
sm com.example.MyClass

# 查找指定方法
sm com.example.MyClass myMethod

# 查看方法的详细信息
sm -d com.example.MyClass myMethod

jad - 反编译

jad命令用于反编译指定的类:

# 反编译类
jad com.example.MyClass

# 反编译指定方法
jad com.example.MyClass myMethod

# 保存反编译结果到文件
jad --source-only com.example.MyClass > MyClass.java

mc - 内存编译

mc (Memory Compiler) 命令用于将.java文件编译成.class文件:

# 编译指定的Java文件
mc /tmp/MyClass.java

# 指定输出目录
mc -d /tmp/output /tmp/MyClass.java

redefine - 热更新

redefine命令用于重新加载类定义,实现不重启应用的Java代码热更新:

# 重新加载类
redefine /tmp/MyClass.class

热更新的典型流程:

  1. 使用jad命令反编译类到文件
  2. 修改Java文件
  3. 使用mc命令编译修改后的Java文件
  4. 使用redefine命令加载新的字节码

监控和诊断命令

monitor - 方法监控

monitor命令用于监控方法的执行情况:

# 监控方法的调用情况
monitor -c 5 com.example.MyClass myMethod

# 监控并设置条件表达式
monitor -c 5 -x 'params[0] > 0' com.example.MyClass myMethod

示例输出:

Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 36 ms, listenerId: 1
 timestamp          class                                           method    total  success  fail  avg-rt(ms)  fail-rate
-------------------------------------------------------------------------------------------------------------
 2022-01-01 12:00:00  com.example.MyClass                            myMethod   10     10      0     5.0        0.00%
 2022-01-01 12:00:05  com.example.MyClass                            myMethod   18     18      0     4.5        0.00%

watch - 方法监听

watch命令用于监听方法的执行数据,如入参、返回值、异常等:

# 监听方法的返回值
watch com.example.MyClass myMethod '{params, returnObj}'

# 监听方法入参和异常
watch com.example.MyClass myMethod '{params, throwExp}' -e

# 监听方法调用前、后、异常和完成时
watch com.example.MyClass myMethod '{params, target, returnObj}' -x 3 -b -e -s -f

参数说明:

  • -b 在方法调用前监听
  • -e 在方法异常时监听
  • -s 在方法返回时监听
  • -f 在方法结束时监听(正常返回或异常返回)
  • -x 指定输出结果的展开层数

trace - 方法调用追踪

trace命令用于追踪方法内部的调用路径和每个调用的耗时:

# 追踪方法调用
trace com.example.MyClass myMethod

# 设置最大追踪深度
trace -n 3 com.example.MyClass myMethod

# 根据耗时过滤
trace com.example.MyClass myMethod '#cost > 10'

示例输出:

`---ts=2022-01-01 12:00:00;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@18b4aac2
    `---[5.0ms] com.example.MyClass:myMethod()
        +---[0.5ms] com.example.MyClass:methodA()
        +---[3.0ms] com.example.MyClass:methodB()
        |   `---[2.5ms] com.example.MyClass:methodC()
        `---[1.0ms] com.example.MyClass:methodD()

stack - 调用栈跟踪

stack命令用于输出当前方法被调用的路径:

# 跟踪方法的调用栈
stack com.example.MyClass myMethod

# 设置最大深度
stack -n 3 com.example.MyClass myMethod

# 根据条件表达式进行过滤
stack com.example.MyClass myMethod 'params[0] > 0'

tt - 时间隧道

tt (Time Tunnel) 命令用于记录方法的调用,并支持回放:

# 记录方法调用
tt -t com.example.MyClass myMethod

# 查看记录的调用
tt -l

# 查看指定ID的调用详情
tt -i 1001

# 重放指定ID的调用
tt -i 1001 -p

脚本和高级功能

options - 全局配置

options命令用于设置Arthas的全局开关:

# 查看所有配置项
options

# 修改单个配置
options unsafe true

profiler - 性能分析

profiler命令支持生成应用热点火焰图:

# 启动分析
profiler start

# 获取分析结果
profiler stop --file /tmp/result.html

# 支持不同的采样方式
profiler start --event cpu
profiler start --event alloc

ognl - 执行OGNL表达式

ognl命令用于执行OGNL表达式,可以动态获取和修改运行中的应用对象:

# 获取静态字段
ognl '@com.example.MyClass@FIELD'

# 调用静态方法
ognl '@com.example.MyClass@staticMethod()'

# 获取SpringContext中的bean
ognl '#springContext=@com.example.Config@applicationContext,#springContext.getBean("beanName")'

实战案例

案例1:定位CPU使用率高的问题

问题描述:线上应用CPU使用率突然飙高,需要快速定位原因。

解决步骤:

  1. 使用dashboard命令查看系统整体情况:
dashboard
  1. 使用thread命令找出CPU使用率高的线程:
thread -n 3
  1. 查看对应线程的调用栈,找到可能的热点方法:
thread 线程ID
  1. 使用trace命令追踪热点方法的执行情况:
trace 热点类 热点方法
  1. 根据结果分析耗时较长的方法,确定CPU高的根本原因。

案例2:定位内存泄漏问题

问题描述:应用运行一段时间后内存持续增长,怀疑存在内存泄漏。

解决步骤:

  1. 使用dashboard命令查看内存使用趋势:
dashboard
  1. 使用heapdump命令生成堆转储文件:
heapdump /tmp/leak.hprof
  1. 使用MAT等工具分析堆转储文件,找出可疑对象。

  2. 使用scsm命令查看可疑类和方法:

sc -d 可疑类
sm -d 可疑类 可疑方法
  1. 使用watch命令监控对象创建情况:
watch 可疑类 可疑方法 '{params, returnObj}' -x 3
  1. 根据监控结果分析内存泄漏的根本原因。

案例3:热修复线上Bug

问题描述:线上应用出现bug,需要在不重启应用的情况下紧急修复。

解决步骤:

  1. 使用jad命令反编译有bug的类:
jad --source-only com.example.BugClass > BugClass.java
  1. 修改Java文件,修复bug。

  2. 使用mc命令编译修改后的Java文件:

mc -d /tmp/output /path/to/BugClass.java
  1. 使用redefine命令热更新类:
redefine /tmp/output/com/example/BugClass.class
  1. 使用watchtrace命令验证修复是否生效:
watch com.example.BugClass bugMethod '{params, returnObj}' -x 3

案例4:排查接口超时问题

问题描述:某个接口偶发性超时,需要定位原因。

解决步骤:

  1. 使用trace命令追踪接口方法的执行过程:
trace com.example.Controller requestMethod '#cost > 1000'
  1. 分析输出结果,找出耗时较长的方法。

  2. 使用stack命令查看耗时方法的调用栈:

stack 耗时类 耗时方法
  1. 使用watch命令监控关键方法的入参和返回值:
watch 耗时类 耗时方法 '{params, returnObj}' -x 3
  1. 根据监控结果分析超时原因,如数据库查询效率低、外部服务调用慢等。

案例5:线程死锁分析

问题描述:应用出现卡顿,怀疑存在线程死锁。

解决步骤:

  1. 使用thread -b命令检查是否存在死锁:
thread -b
  1. 如果存在死锁,查看死锁线程的详细信息:
thread 死锁线程ID
  1. 分析死锁线程的堆栈信息,找出死锁的资源和代码位置。

  2. 使用jad命令反编译相关类,查看锁的获取逻辑:

jad 死锁相关类
  1. 根据分析结果,制定死锁解决方案。

最佳实践与注意事项

性能影响

虽然Arthas是一个强大的工具,但使用不当可能会对线上应用造成性能影响:

  1. 避免长时间运行:如tracemonitor等命令会增加方法调用的开销,应避免长时间运行。

  2. 合理设置条件表达式:使用条件表达式过滤掉不需要的调用,减少数据收集量。

  3. 使用采样:对于高频调用的方法,可以使用采样参数减少采集频率。

  4. 避免对频繁调用的方法使用重量级命令:如watch命令观察每次参数和返回值会产生大量数据。

安全建议

在生产环境使用Arthas需要注意以下安全事项:

  1. 访问控制:确保Arthas服务的端口不对外网开放,必要时设置认证。

  2. 权限管理:使用Arthas的用户应具有相应的权限,避免非授权访问。

  3. 避免修改业务数据:使用ognl等命令时,避免修改关键业务数据或状态。

  4. 谨慎使用热更新redefine命令虽然强大,但可能引入新的问题,应在充分测试后使用。

使用技巧

  1. 使用管道和grep过滤结果
thread | grep 'waiting for monitor'
  1. 保存命令输出到文件
jad --source-only com.example.MyClass > MyClass.java
  1. 使用批处理模式
telnet localhost 3658 < commands.txt
  1. 使用Web Console:Arthas提供了Web界面,更直观地查看结果。

  2. 构建命令别名:对于常用的复杂命令,可以创建别名简化使用。

总结

Arthas作为一款功能强大的Java应用诊断工具,为开发人员提供了一种非侵入式的方式来分析和解决线上问题。通过本文的介绍和实战案例,相信大家已经对Arthas的核心功能和使用方法有了深入的了解。

在实际工作中,掌握Arthas可以帮助我们:

  • 快速定位线上问题,减少平均故障修复时间(MTTR)
  • 在不重启应用的情况下修复紧急Bug
  • 深入分析应用性能瓶颈,持续优化系统性能
  • 增强对JVM和应用运行机制的理解

最后,需要强调的是,虽然Arthas非常强大,但它是一把"双刃剑",在生产环境中使用时应当谨慎,遵循"先观察、后修改"的原则,确保操作的安全性。

参考资源

Windows激活完整指南:MAS工具详解与多种激活方法