Arthas线上问题排查完全指南
目录
一、Arthas简介
1.1 什么是Arthas?
Arthas 是阿里巴巴开源的Java诊断工具,当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
- 这个类从哪个jar包加载的?为什么会报各种类相关的Exception?
- 我改的代码为什么没有执行到?难道是我没commit?分支搞错了?
- 遇到问题无法在线上debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到JVM的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
1.2 Arthas的核心功能
| 功能分类 | 具体能力 | 使用场景 |
|---|---|---|
| 类加载 | 查看类加载信息、类方法 | 类冲突、方法找不到 |
| 方法监控 | 监控方法调用、参数、返回值 | 接口慢、逻辑错误 |
| 性能分析 | CPU、内存、线程分析 | 性能问题、资源占用 |
| 热更新 | 动态修改类、重新加载 | 紧急修复线上bug |
| JVM监控 | 实时查看JVM状态 | 系统整体监控 |
| 火焰图 | 生成调用链火焰图 | 性能瓶颈定位 |
1.3 Arthas vs 传统工具
| 对比项 | 传统方式 | Arthas |
|---|---|---|
| 日志排查 | 加日志→打包→发布→等待复现 | 实时查看方法参数和返回值 |
| 性能问题 | jstack、jmap多次执行 | dashboard一键查看 |
| 类加载问题 | 手动分析jar包 | sc/sm命令直接查看 |
| 线上调试 | 无法调试 | watch/trace实时监控 |
| 热修复 | 必须重启 | redefine动态修改 |
1.4 适用场景
✅ 适合用Arthas的场景:
- 线上问题快速定位
- 性能瓶颈分析
- 类加载问题排查
- 方法调用追踪
- JVM状态监控
- 热修复紧急bug
❌ 不适合用Arthas的场景:
- 生产环境长期监控(会有性能开销)
- 需要持久化的监控数据(应使用APM系统)
- 自动化运维(Arthas更偏向手动操作)
二、安装与启动
2.1 下载安装
方式一:快速安装(推荐)
# 下载arthas-boot.jar
curl -O https://arthas.aliyun.com/arthas-boot.jar
# 或者使用wget
wget https://arthas.aliyun.com/arthas-boot.jar
# 启动
java -jar arthas-boot.jar
方式二:全量安装
# 下载完整包
curl -O https://arthas.aliyun.com/arthas-packaging-latest.zip
# 解压
unzip arthas-packaging-latest.zip
# 启动
./bin/as.sh
方式三:使用as.sh在线安装
# 下载as.sh
curl -L https://arthas.aliyun.com/install.sh | sh
# 启动
./as.sh
2.2 启动Arthas
步骤1:查找Java进程
$ java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.7.1
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 12345 com.demoCode.edu.backend.DataQualityMonitorApplication
[2]: 67890 org.apache.catalina.startup.Bootstrap
步骤2:选择目标进程
# 输入进程编号(如1)
1
[INFO] arthas home: /root/.arthas/lib/3.7.1/arthas
[INFO] Try to attach process 12345
[INFO] Attach process 12345 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.7.1
main_class com.demoCode.edu.backend.DataQualityMonitorApplication
pid 12345
time 2024-01-15 10:30:45
[arthas@12345]$
步骤3:开始使用
# 查看帮助
[arthas@12345]$ help
# 查看仪表盘
[arthas@12345]$ dashboard
2.3 连接方式
本地连接
java -jar arthas-boot.jar
远程连接
# 启动时指定监听端口
java -jar arthas-boot.jar --telnet-port 9999 --http-port 8563
# 从其他机器连接
telnet <server-ip> 9999
Web Console(推荐)
# 启动后访问
http://<server-ip>:8563
# 在浏览器中使用Arthas
2.4 退出Arthas
# 退出当前客户端(不影响Arthas服务端)
quit
# 或使用exit
exit
# 完全关闭Arthas服务端
stop
# 查看帮助
help
三、核心命令详解
3.1 基础命令
3.1.1 dashboard - 实时数据面板⭐⭐⭐⭐⭐
作用: 查看系统实时数据面板,包括线程、内存、GC、运行环境等信息
[arthas@12345]$ dashboard
输出示例:
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTE DAEMON
17 pool-2-thread-1 main 5 WAITING 0.0 0.000 0:0.000 false false
27 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.000 false true
11 AsyncAppender-Worker-1 main 5 WAITING 0.0 0.000 0:0.015 false true
48 http-nio-8080-exec-1 main 5 WAITING 0.0 0.000 0:0.234 false true
Memory used total max usage
heap 128M 256M 4096M 3.13%
ps_eden_space 45M 76M 1536M 2.93%
ps_survivor_space 0M 12M 12M 0.00%
ps_old_gen 83M 168M 3072M 2.70%
GC COUNT TIME(ms)
ps_scavenge 5 12
ps_marksweep 0 0
Runtime
os.name Linux
os.version 3.10.0
java.version 1.8.0_191
java.home /usr/local/java/jdk1.8.0_191/jre
参数说明:
-i <interval>:刷新间隔(默认5秒)-n <times>:刷新次数
# 每2秒刷新一次,共刷新10次
dashboard -i 2000 -n 10
3.1.2 thread - 线程信息查看⭐⭐⭐⭐⭐
作用: 查看线程堆栈信息,找出最繁忙的线程,定位线程死锁等
基本用法:
# 查看所有线程
thread
# 查看指定线程
thread <线程ID>
# 查看CPU使用率最高的N个线程
thread -n 3
# 查看所有线程堆栈
thread -all
# 查找死锁
thread -b
示例1:查看CPU使用率最高的3个线程
[arthas@12345]$ thread -n 3
"http-nio-8080-exec-10" Id=123 RUNNABLE
at com.demoCode.edu.backend.service.AnalysisTaskService.processData(AnalysisTaskService.java:156)
at com.demoCode.edu.backend.controller.AnalysisTaskController.analyze(AnalysisTaskController.java:89)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
示例2:查找死锁
[arthas@12345]$ thread -b
"Thread-1" Id=15 BLOCKED on java.lang.Object@3d4eac69 owned by "Thread-2" Id=16
at com.example.DeadLockDemo.method1(DeadLockDemo.java:20)
"Thread-2" Id=16 BLOCKED on java.lang.Object@7f31245a owned by "Thread-1" Id=15
at com.example.DeadLockDemo.method2(DeadLockDemo.java:30)
示例3:查看某个线程的详细信息
[arthas@12345]$ thread 123
"http-nio-8080-exec-10" Id=123 RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
...
应用场景:
- CPU飙高:
thread -n 3找出占用CPU最高的线程 - 死锁问题:
thread -b快速定位死锁 - 线程阻塞:查看具体线程的堆栈
3.1.3 jvm - JVM信息查看⭐⭐⭐⭐
作用: 查看JVM详细信息
[arthas@12345]$ jvm
RUNTIME
----------------------------------------
MACHINE-NAME 12345@hostname
JVM-START-TIME 2024-01-15 09:00:00
MANAGEMENT-SPEC-VERSION 1.2
SPEC-NAME Java Virtual Machine Specification
SPEC-VENDOR Oracle Corporation
SPEC-VERSION 1.8
VM-NAME Java HotSpot(TM) 64-Bit Server VM
VM-VENDOR Oracle Corporation
VM-VERSION 25.191-b12
INPUT-ARGUMENTS -Xms512m
-Xmx4g
-XX:+UseG1GC
CLASS-LOADING
----------------------------------------
LOADED-CLASS-COUNT 8756
TOTAL-LOADED-CLASS-COUNT 8756
UNLOADED-CLASS-COUNT 0
MEMORY
----------------------------------------
HEAP-MEMORY-USAGE
init: 536870912(512.0 MiB)
used: 134217728(128.0 MiB)
committed: 268435456(256.0 MiB)
max: 4294967296(4.0 GiB)
3.2 类相关命令
3.2.1 sc - 查找类⭐⭐⭐⭐⭐
作用: Search-Class,查找已加载的类
基本用法:
# 查找类
sc <类名模式>
# 查看类的详细信息
sc -d <类名>
# 查看类的字段信息
sc -d -f <类名>
示例1:模糊搜索
[arthas@12345]$ sc *AnalysisTaskController
com.demoCode.edu.backend.controller.AnalysisTaskController
示例2:查看类详细信息
[arthas@12345]$ sc -d com.demoCode.edu.backend.controller.AnalysisTaskController
class-info com.demoCode.edu.backend.controller.AnalysisTaskController
code-source /app/data-quality-monitor.jar
name com.demoCode.edu.backend.controller.AnalysisTaskController
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name AnalysisTaskController
modifier public
annotation org.springframework.web.bind.annotation.RestController
org.springframework.web.bind.annotation.RequestMapping
interfaces
super-class java.lang.Object
class-loader org.springframework.boot.loader.LaunchedURLClassLoader@6d6f6e28
classLoaderHash 6d6f6e28
示例3:查找类的所有字段
[arthas@12345]$ sc -d -f com.demoCode.edu.backend.service.AnalysisTaskService
class-info com.demoCode.edu.backend.service.AnalysisTaskService
code-source /app/data-quality-monitor.jar
...
declared-fields
private static final org.slf4j.Logger logger
@org.springframework.beans.factory.annotation.Autowired
private com.demoCode.edu.backend.mapper.AnalysisTaskMapper analysisTaskMapper
@org.springframework.beans.factory.annotation.Autowired
private com.demoCode.edu.backend.service.DataProcessService dataProcessService
应用场景:
- 类冲突排查:查看类从哪个jar包加载
- 验证部署:确认类是否是最新版本
- 类信息查看:了解类的结构
3.2.2 sm - 查找方法⭐⭐⭐⭐⭐
作用: Search-Method,查找类的方法
基本用法:
# 查找类的所有方法
sm <类名>
# 查找特定方法
sm <类名> <方法名>
# 查看方法详细信息
sm -d <类名> <方法名>
示例1:查看类的所有方法
[arthas@12345]$ sm com.demoCode.edu.backend.controller.AnalysisTaskController
com.demoCode.edu.backend.controller.AnalysisTaskController <init>()V
com.demoCode.edu.backend.controller.AnalysisTaskController analyze(Lcom/demoCode/edu/backend/dto/AnalysisRequest;)Lcom/demoCode/edu/common/Result;
com.demoCode.edu.backend.controller.AnalysisTaskController getTaskList(Ljava/lang/Integer;Ljava/lang/Integer;)Lcom/demoCode/edu/common/Result;
com.demoCode.edu.backend.controller.AnalysisTaskController deleteTask(Ljava/lang/Long;)Lcom/demoCode/edu/common/Result;
示例2:查找特定方法
[arthas@12345]$ sm com.demoCode.edu.backend.controller.AnalysisTaskController analyze
com.demoCode.edu.backend.controller.AnalysisTaskController analyze(Lcom/demoCode/edu/backend/dto/AnalysisRequest;)Lcom/demoCode/edu/common/Result;
示例3:查看方法详细信息
[arthas@12345]$ sm -d com.demoCode.edu.backend.controller.AnalysisTaskController analyze
declaring-class com.demoCode.edu.backend.controller.AnalysisTaskController
method-name analyze
modifier public
annotation org.springframework.web.bind.annotation.PostMapping
parameters com.demoCode.edu.backend.dto.AnalysisRequest
return com.demoCode.edu.common.Result
exceptions
classLoaderHash 6d6f6e28
3.2.3 jad - 反编译⭐⭐⭐⭐⭐
作用: 反编译指定已加载类的源码
基本用法:
# 反编译类
jad <类名>
# 反编译指定方法
jad <类名> <方法名>
# 反编译并输出到文件
jad --source-only <类名> > /tmp/ClassName.java
示例1:反编译类
[arthas@12345]$ jad com.demoCode.edu.backend.controller.AnalysisTaskController
ClassLoader:
+-org.springframework.boot.loader.LaunchedURLClassLoader@6d6f6e28
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@5e2de80c
Location:
/app/data-quality-monitor.jar
/*
* Decompiled with CFR.
*/
package com.demoCode.edu.backend.controller;
@RestController
@RequestMapping(value={"/api/analysis"})
public class AnalysisTaskController {
@Autowired
private AnalysisTaskService analysisTaskService;
@PostMapping(value={"/analyze"})
public Result analyze(@RequestBody AnalysisRequest request) {
return this.analysisTaskService.analyze(request);
}
...
}
示例2:反编译指定方法
[arthas@12345]$ jad com.demoCode.edu.backend.service.AnalysisTaskService processData
/*
* Decompiled with CFR.
*/
public void processData(Long taskId) {
DmAttachFileInfo attachFileInfo = null;
try {
attachFileInfo = this.getFileInfo(taskId);
// ... 省略代码
} catch (Exception e) {
log.error("处理数据异常", e);
}
}
示例3:只输出源码(不含其他信息)
[arthas@12345]$ jad --source-only com.demoCode.edu.backend.controller.AnalysisTaskController > /tmp/AnalysisTaskController.java
应用场景:
- 验证代码是否生效(最常用)
- 查看线上实际运行的代码
- 对比本地代码和线上代码的差异
3.3 监控命令
3.3.1 watch - 方法监控⭐⭐⭐⭐⭐
作用: 观察方法的入参、返回值、异常等
基本语法:
watch <类名> <方法名> <观察表达式> <条件表达式> -x <展开层级>
观察表达式说明:
params:方法入参数组returnObj:方法返回值throwExp:方法抛出的异常target:当前对象clazz:类对象method:方法对象
示例1:观察方法入参和返回值
[arthas@12345]$ watch com.demoCode.edu.backend.controller.AnalysisTaskController analyze "{params,returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 45 ms, listenerId: 1
method=com.demoCode.edu.backend.controller.AnalysisTaskController.analyze
location=AtExit
ts=2024-01-15 10:45:23; [cost=125.6ms]
result=@ArrayList[
@Object[][
@AnalysisRequest[
AnalysisRequest{taskId=123, dataType='excel'}
],
],
@Result[
Result{code=200, message='success', data=...}
],
]
示例2:观察异常
[arthas@12345]$ watch com.demoCode.edu.backend.service.AnalysisTaskService processData "{params,throwExp}" -e -x 2
# -e 表示只有抛异常时才输出
# 当方法抛异常时输出:
method=com.demoCode.edu.backend.service.AnalysisTaskService.processData
location=AtExceptionExit
ts=2024-01-15 10:46:15; [cost=12.5ms]
result=@ArrayList[
@Object[][
@Long[123],
],
java.lang.NullPointerException
at com.demoCode.edu.backend.service.AnalysisTaskService.processData(AnalysisTaskService.java:156)
...
]
示例3:条件过滤
# 只观察taskId为123的调用
[arthas@12345]$ watch com.demoCode.edu.backend.service.AnalysisTaskService processData "{params,returnObj}" "params[0]==123" -x 2
# 只观察返回值不为null的调用
[arthas@12345]$ watch com.demoCode.edu.backend.service.AnalysisTaskService getData "{params,returnObj}" "returnObj!=null" -x 2
示例4:观察方法执行前后
# -b: 方法调用前
# -s: 方法调用成功后
# -e: 方法抛异常时
# -f: 方法调用完成后(无论成功失败)
# 观察方法调用前的入参
[arthas@12345]$ watch com.demoCode.edu.backend.service.AnalysisTaskService processData "{params}" -b -x 2
# 观察方法调用后的返回值
[arthas@12345]$ watch com.demoCode.edu.backend.service.AnalysisTaskService processData "{returnObj}" -s -x 2
常用参数:
-x <value>:指定输出结果的展开层级,默认1-b:在方法调用之前观察-e:在方法抛异常时观察-s:在方法返回之后观察-f:在方法结束之后观察(无论正常返回还是异常)-n <value>:只执行N次
应用场景:
- 实时查看方法入参和返回值(替代日志)
- 排查方法异常问题
- 验证方法是否被调用
- 性能问题定位
3.3.2 trace - 方法调用链路⭐⭐⭐⭐⭐
作用: 追踪方法内部调用路径,并输出每个节点的耗时
基本用法:
trace <类名> <方法名>
# 指定条件
trace <类名> <方法名> <条件表达式>
# 包含jdk方法
trace <类名> <方法名> --skipJDKMethod false
# 执行N次
trace <类名> <方法名> -n 5
示例1:追踪方法调用路径
[arthas@12345]$ trace com.demoCode.edu.backend.controller.AnalysisTaskController analyze
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 2
`---ts=2024-01-15 10:50:23;thread_name=http-nio-8080-exec-5;id=2f;is_daemon=true;priority=5;TCCL=org.springframework.boot.loader.LaunchedURLClassLoader@6d6f6e28
`---[125.6ms] com.demoCode.edu.backend.controller.AnalysisTaskController:analyze()
+---[0.05ms] com.demoCode.edu.backend.dto.AnalysisRequest:getTaskId()
+---[0.03ms] com.demoCode.edu.backend.dto.AnalysisRequest:getDataType()
`---[125.2ms] com.demoCode.edu.backend.service.AnalysisTaskService:analyze()
+---[2.3ms] com.demoCode.edu.backend.service.AnalysisTaskService:validateRequest()
+---[15.6ms] com.demoCode.edu.backend.mapper.AnalysisTaskMapper:selectById()
+---[105.8ms] com.demoCode.edu.backend.service.AnalysisTaskService:processData()
| +---[1.2ms] com.demoCode.edu.backend.service.FileService:getFileInfo()
| `---[104.5ms] com.demoCode.edu.backend.service.DataProcessService:process()
`---[1.5ms] com.demoCode.edu.backend.mapper.AnalysisTaskMapper:updateById()
示例2:添加条件过滤
# 只追踪耗时超过100ms的调用
[arthas@12345]$ trace com.demoCode.edu.backend.controller.AnalysisTaskController analyze '#cost > 100'
示例3:多层追踪
# 同时追踪多个方法
[arthas@12345]$ trace -E com.demoCode.edu.backend.service.*Service analyze|process
应用场景:
- 接口慢问题定位(找出具体慢在哪个方法)
- 分析方法调用链路
- 性能优化前的性能分析
3.3.3 monitor - 方法监控统计⭐⭐⭐⭐
作用: 监控方法的调用次数、成功次数、失败次数、平均耗时等统计信息
基本用法:
monitor <类名> <方法名> -c <周期秒数>
示例:
[arthas@12345]$ monitor -c 5 com.demoCode.edu.backend.controller.AnalysisTaskController analyze
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 45 ms, listenerId: 3
timestamp class method total success fail avg-rt(ms) fail-rate
------------------------------------------------------------------------------------------------------------------------------------
2024-01-15 10:55:10 AnalysisTaskController analyze 15 14 1 125.6 6.67%
2024-01-15 10:55:15 AnalysisTaskController analyze 23 23 0 98.3 0.00%
2024-01-15 10:55:20 AnalysisTaskController analyze 18 17 1 156.7 5.56%
字段说明:
total:调用总次数success:成功次数fail:失败次数avg-rt:平均响应时间fail-rate:失败率
应用场景:
- 实时监控接口调用情况
- 统计接口成功率
- 监控接口性能
3.3.4 stack - 方法调用堆栈⭐⭐⭐
作用: 查看方法被调用的路径
基本用法:
stack <类名> <方法名>
# 添加条件
stack <类名> <方法名> <条件表达式>
示例:
[arthas@12345]$ stack com.demoCode.edu.backend.service.DataProcessService process
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 45 ms, listenerId: 4
ts=2024-01-15 11:00:23;thread_name=http-nio-8080-exec-10;id=3f;is_daemon=true;priority=5;TCCL=org.springframework.boot.loader.LaunchedURLClassLoader@6d6f6e28
@com.demoCode.edu.backend.service.DataProcessService.process()
at com.demoCode.edu.backend.service.AnalysisTaskService.processData(AnalysisTaskService.java:156)
at com.demoCode.edu.backend.service.AnalysisTaskService.analyze(AnalysisTaskService.java:89)
at com.demoCode.edu.backend.controller.AnalysisTaskController.analyze(AnalysisTaskController.java:106)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
...
应用场景:
- 查看方法被谁调用
- 分析调用链路
- 排查代码执行路径
3.3.5 tt - 时光隧道⭐⭐⭐⭐
作用: 记录方法调用的现场,可以随时重放
基本用法:
# 记录方法调用
tt -t <类名> <方法名>
# 查看记录列表
tt -l
# 查看某次调用详情
tt -i <索引>
# 重放某次调用
tt -i <索引> -p
示例1:记录方法调用
[arthas@12345]$ tt -t com.demoCode.edu.backend.controller.AnalysisTaskController analyze
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 45 ms, listenerId: 5
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
--------------------------------------------------------------------------------------------------------------------
1000 2024-01-15 11:05:23 125.6 true false 0x7f312456 AnalysisTaskController analyze
1001 2024-01-15 11:05:28 98.3 true false 0x7f312456 AnalysisTaskController analyze
1002 2024-01-15 11:05:33 156.7 true false 0x7f312456 AnalysisTaskController analyze
示例2:查看某次调用的详细信息
[arthas@12345]$ tt -i 1000
INDEX 1000
GMT-CREATE 2024-01-15 11:05:23
COST(ms) 125.6
OBJECT 0x7f312456
CLASS com.demoCode.edu.backend.controller.AnalysisTaskController
METHOD analyze
IS-RETURN true
IS-EXCEPTION false
PARAMETERS[0] @AnalysisRequest[
AnalysisRequest{taskId=123, dataType='excel'}
]
RETURN-OBJ @Result[
Result{code=200, message='success'}
]
示例3:重放某次调用
# 使用记录的参数重新调用方法
[arthas@12345]$ tt -i 1000 -p
Re-invoke result:
@Result[
Result{code=200, message='success'}
]
示例4:过滤记录
# 只记录异常的调用
[arthas@12345]$ tt -t com.demoCode.edu.backend.service.AnalysisTaskService processData -e
# 只记录耗时超过100ms的调用
[arthas@12345]$ tt -t com.demoCode.edu.backend.controller.AnalysisTaskController analyze '#cost > 100'
应用场景:
- 记录线上问题现场,线下重放
- 测试代码修改效果(修改后重放)
- 压力测试(重放多次)
3.4 高级命令
3.4.1 profiler - 火焰图生成⭐⭐⭐⭐⭐
作用: 生成CPU火焰图,快速定位性能瓶颈
基本用法:
# 启动采样
profiler start
# 查看采样状态
profiler status
# 停止采样并生成火焰图
profiler stop --format html
# 生成到指定文件
profiler stop --file /tmp/flamegraph.html
示例:
# 1. 启动采样
[arthas@12345]$ profiler start
Started [cpu] profiling
# 2. 等待一段时间(如30秒),让系统运行
# 3. 查看状态
[arthas@12345]$ profiler status
[cpu] profiling is running for 30 seconds
# 4. 停止并生成火焰图
[arthas@12345]$ profiler stop --format html
profiler output file: /tmp/arthas-output/20240115-110523.html
查看火焰图:
# 下载到本地
scp user@server:/tmp/arthas-output/20240115-110523.html ./
# 在浏览器中打开
open 20240115-110523.html
火焰图解读:
- X轴:按字母排序的函数名称
- Y轴:调用栈深度
- 颜色:随机,无特殊含义
- 宽度:CPU占用时间(越宽越慢)
应用场景:
- CPU使用率高,定位热点方法
- 性能优化前的性能分析
- 找出程序的性能瓶颈
3.4.2 redefine - 热更新⭐⭐⭐⭐
作用: 加载外部的.class文件,替换JVM中已加载的类
使用步骤:
1. 编译修改后的类
# 本地修改代码后编译
javac -cp /path/to/lib/*.jar AnalysisTaskController.java
2. 上传到服务器
scp AnalysisTaskController.class user@server:/tmp/
3. 使用redefine加载
[arthas@12345]$ redefine /tmp/AnalysisTaskController.class
redefine success, size: 1, classes:
com.demoCode.edu.backend.controller.AnalysisTaskController
4. 验证是否生效
# 反编译查看
[arthas@12345]$ jad com.demoCode.edu.backend.controller.AnalysisTaskController
# 应该能看到修改后的代码
注意事项:
- ⚠️ redefine有限制,不能添加/删除字段和方法,只能修改方法体
- ⚠️ 重启后会失效,需要重新部署
- ⚠️ 仅用于紧急修复,不能作为常规发布手段
应用场景:
- 紧急修复线上bug
- 临时添加日志排查问题
- 快速验证代码修改
3.4.3 ognl - 执行表达式⭐⭐⭐
作用: 执行OGNL表达式,可以调用静态方法、获取Spring Bean等
基本用法:
ognl <表达式>
# 指定ClassLoader
ognl -c <hashcode> <表达式>
示例1:调用静态方法
# 获取当前时间
[arthas@12345]$ ognl '@java.lang.System@currentTimeMillis()'
@Long[1705295123456]
# 调用自定义静态方法
[arthas@12345]$ ognl '@com.demoCode.edu.util.DateUtil@formatDate(new java.util.Date())'
@String[2024-01-15 11:12:03]
示例2:获取Spring Bean
# 获取ApplicationContext
[arthas@12345]$ ognl '#springContext=@com.demoCode.edu.backend.Application@applicationContext'
# 获取Bean
[arthas@12345]$ ognl '#springContext.getBean("analysisTaskService")'
@AnalysisTaskService[
analysisTaskMapper=@AnalysisTaskMapper[...],
...
]
# 调用Bean的方法
[arthas@12345]$ ognl '#springContext.getBean("analysisTaskService").getTaskById(123L)'
示例3:查看系统属性
# 获取所有系统属性
[arthas@12345]$ ognl '@java.lang.System@getProperties()'
# 获取特定属性
[arthas@12345]$ ognl '@java.lang.System@getProperty("java.version")'
@String[1.8.0_191]
应用场景:
- 调试:执行代码片段
- 获取Spring Bean状态
- 修改配置(谨慎使用)
- 调用工具方法
四、典型问题排查场景
4.1 CPU使用率高⭐⭐⭐⭐⭐
问题描述: 服务器CPU使用率突然飙升到100%
排查步骤:
1. 找出占用CPU最高的线程
[arthas@12345]$ thread -n 3
"http-nio-8080-exec-23" Id=145 cpu=89.5% RUNNABLE
at com.demoCode.edu.backend.service.DataProcessService.processData(DataProcessService.java:234)
at com.demoCode.edu.backend.service.AnalysisTaskService.analyze(AnalysisTaskService.java:89)
...
2. 追踪该线程正在执行的方法
[arthas@12345]$ trace com.demoCode.edu.backend.service.DataProcessService processData
`---[8500.2ms] com.demoCode.edu.backend.service.DataProcessService:processData()
`---[8498.5ms] com.demoCode.edu.backend.service.DataProcessService:calculateResult()
`---[8495.3ms] com.demoCode.edu.backend.util.MathUtil:factorial() <--- 发现这里耗时最长
3. 查看具体代码
[arthas@12345]$ jad com.demoCode.edu.backend.util.MathUtil factorial
public static long factorial(int n) {
// 发现是死循环或者复杂度过高的算法
long result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
// 可能n非常大,导致计算很久
}
return result;
}
4. 生成火焰图进一步分析
[arthas@12345]$ profiler start
[arthas@12345]$ profiler stop --format html --file /tmp/cpu-flamegraph.html
5. 临时热修复(如果可能)
# 修改代码添加日志或限制
# 编译后上传
[arthas@12345]$ redefine /tmp/MathUtil.class
根因定位:
- 某个算法复杂度过高
- 死循环
- 正则表达式回溯
- 大量反射操作
4.2 接口响应慢⭐⭐⭐⭐⭐
问题描述: 某个接口响应时间从100ms增加到5秒
排查步骤:
1. 使用trace追踪整个调用链路
[arthas@12345]$ trace com.demoCode.edu.backend.controller.AnalysisTaskController analyze
`---[5234.6ms] com.demoCode.edu.backend.controller.AnalysisTaskController:analyze()
+---[0.05ms] 参数验证
`---[5234.2ms] com.demoCode.edu.backend.service.AnalysisTaskService:analyze()
+---[0.8ms] validateRequest()
+---[15.6ms] selectById() - 数据库查询
+---[5215.3ms] processData() <--- 发现这里最慢
`---[2.5ms] updateById()
2. 深入追踪慢方法
[arthas@12345]$ trace com.demoCode.edu.backend.service.AnalysisTaskService processData
`---[5215.3ms] processData()
+---[1.2ms] getFileInfo()
+---[5210.5ms] dataProcessService.process() <--- 继续追踪
`---[3.6ms] saveResult()
3. 再次追踪
[arthas@12345]$ trace com.demoCode.edu.backend.service.DataProcessService process
`---[5210.5ms] process()
+---[5.6ms] parseData()
+---[5200.2ms] httpClient.post() <--- 找到根因:HTTP调用慢
`---[4.7ms] formatResult()
4. 查看HTTP调用的参数和返回值
[arthas@12345]$ watch com.demoCode.edu.backend.service.DataProcessService process "{params,returnObj,#cost}" -x 3
result=@ArrayList[
@Object[][
@Data[...大量数据...], <--- 发现传输数据量很大
],
@Result[...],
@Integer[5200], <--- 耗时5200ms
]
5. 监控该方法的调用情况
[arthas@12345]$ monitor -c 5 com.demoCode.edu.backend.service.DataProcessService process
timestamp method total success fail avg-rt(ms)
-----------------------------------------------------------------
2024-01-15 11:30:10 process 3 3 0 5234.6
2024-01-15 11:30:15 process 2 2 0 5156.8
根因定位:
- 外部HTTP接口慢
- 数据量过大导致传输慢
- 需要优化:减少数据量、增加缓存、异步处理
4.3 内存泄漏⭐⭐⭐⭐
问题描述: 内存使用率持续增长,最终OOM
排查步骤:
1. 查看内存使用情况
[arthas@12345]$ dashboard
Memory used total max usage
heap 3.8G 4.0G 4.0G 95.00% <--- 内存快满了
ps_eden_space 1.2G 1.4G 1.4G 85.71%
ps_old_gen 2.6G 2.6G 2.6G 100.00% <--- 老年代满了
2. 查看GC情况
[arthas@12345]$ dashboard
GC COUNT TIME(ms)
ps_scavenge 1523 5234
ps_marksweep 45 8967 <--- Full GC次数多,时间长
3. dump堆内存
[arthas@12345]$ heapdump /tmp/heap.hprof
Dumping heap to /tmp/heap.hprof...
Heap dump file created [4294967296 bytes in 5.234 secs]
4. 下载到本地分析
scp user@server:/tmp/heap.hprof ./
# 使用MAT或VisualVM分析
# 找出占用内存最多的对象
5. 查看可能泄漏的对象
# 假设分析发现是某个缓存导致的
[arthas@12345]$ ognl '#springContext.getBean("cacheManager").getCache("dataCache").size()'
@Integer[1234567] <--- 缓存太大了
6. 临时清理(紧急情况)
[arthas@12345]$ ognl '#springContext.getBean("cacheManager").getCache("dataCache").clear()'
根因定位:
- 缓存无限增长未清理
- 对象持有大量引用未释放
- 静态集合不断添加对象
- 线程池任务队列无限增长
4.4 线程死锁⭐⭐⭐⭐
问题描述: 应用卡住,部分请求无响应
排查步骤:
1. 检查是否有死锁
[arthas@12345]$ thread -b
"Thread-1" Id=15 BLOCKED on java.lang.Object@3d4eac69 owned by "Thread-2" Id=16
at com.demoCode.edu.backend.service.OrderService.createOrder(OrderService.java:45)
- waiting to lock <0x00000007f3d4eac69> (a java.lang.Object)
- locked <0x00000007f7f31245a> (a java.lang.Object)
"Thread-2" Id=16 BLOCKED on java.lang.Object@7f31245a owned by "Thread-1" Id=15
at com.demoCode.edu.backend.service.InventoryService.decreaseStock(InventoryService.java:30)
- waiting to lock <0x00000007f7f31245a> (a java.lang.Object)
- locked <0x00000007f3d4eac69> (a java.lang.Object)
Found one Java-level deadlock:
2. 查看死锁线程的详细信息
[arthas@12345]$ thread 15
"Thread-1" Id=15 BLOCKED
at com.demoCode.edu.backend.service.OrderService.createOrder(OrderService.java:45)
at com.demoCode.edu.backend.controller.OrderController.create(OrderController.java:23)
...
3. 反编译查看死锁代码
[arthas@12345]$ jad com.demoCode.edu.backend.service.OrderService createOrder
public void createOrder() {
synchronized(lockA) { // 先锁A
// ...
synchronized(lockB) { // 再锁B
// ...
}
}
}
[arthas@12345]$ jad com.demoCode.edu.backend.service.InventoryService decreaseStock
public void decreaseStock() {
synchronized(lockB) { // 先锁B
// ...
synchronized(lockA) { // 再锁A <--- 死锁原因:锁顺序不一致
// ...
}
}
}
根因定位:
- 两个线程以不同的顺序获取锁
- 需要修改代码保证锁的获取顺序一致
4.5 方法未执行/代码未生效⭐⭐⭐⭐⭐
问题描述: 代码明明改了并且发布了,但是线上行为没有变化
排查步骤:
1. 反编译查看线上代码
[arthas@12345]$ jad com.demoCode.edu.backend.controller.AnalysisTaskController analyze
@PostMapping("/analyze")
public Result analyze(@RequestBody AnalysisRequest request) {
log.info("开始分析..."); // <--- 发现没有新加的日志
return analysisTaskService.analyze(request);
}
2. 查看类加载信息
[arthas@12345]$ sc -d com.demoCode.edu.backend.controller.AnalysisTaskController
class-info com.demoCode.edu.backend.controller.AnalysisTaskController
code-source /app/data-quality-monitor-old.jar <--- 发现加载的是旧jar包
...
3. 检查是否有多个版本的类
[arthas@12345]$ sc -d *AnalysisTaskController
com.demoCode.edu.backend.controller.AnalysisTaskController
code-source: /app/data-quality-monitor-old.jar
classLoaderHash: 6d6f6e28
# 如果有多个,说明有类冲突
4. 使用watch验证方法是否被调用
[arthas@12345]$ watch com.demoCode.edu.backend.controller.AnalysisTaskController analyze "{params,returnObj}" -x 2
# 如果请求发过来但没有输出,说明方法没被调用
# 可能是路由配置错误或者被拦截了
根因定位:
- jar包版本错误
- 类加载器冲突
- 代码被缓存(需要清理缓存)
- 路由配置错误
4.6 类找不到/NoSuchMethodError⭐⭐⭐⭐
问题描述: 应用启动或运行时抛出ClassNotFoundException或NoSuchMethodError
排查步骤:
1. 查找类是否存在
[arthas@12345]$ sc *FastJsonUtil
# 如果没有输出,说明类确实不存在
# 如果有输出,继续下一步
2. 查看类的详细信息
[arthas@12345]$ sc -d com.alibaba.fastjson.JSON
class-info com.alibaba.fastjson.JSON
code-source /app/lib/fastjson-1.2.47.jar <--- 可能版本太低
classLoaderHash 6d6f6e28
3. 查找方法是否存在
[arthas@12345]$ sm com.alibaba.fastjson.JSON parseObject
# 没有找到该方法,说明版本不匹配
4. 查找所有版本的fastjson
# 在服务器上查找
find /app -name "fastjson*.jar"
/app/lib/fastjson-1.2.47.jar
/app/lib/commons-lib/fastjson-1.2.28.jar <--- 发现有两个版本
# 可能加载了低版本的jar
5. 查看类的加载路径
[arthas@12345]$ sc -d com.alibaba.fastjson.JSON | grep code-source
code-source /app/lib/commons-lib/fastjson-1.2.28.jar <--- 确认加载的是低版本
根因定位:
- jar包版本冲突
- 依赖传递导致加载了低版本
- 需要排除低版本依赖或升级
五、高级使用技巧
5.1 结合其他工具使用
1. 结合grep过滤
# 查找包含特定关键字的类
sc *Service | grep Analysis
# 查找包含特定方法的类
sm *Service * | grep process
2. 结合wc统计
# 统计类的数量
sc * | wc -l
# 统计方法数量
sm com.demoCode.edu.backend.service.AnalysisTaskService * | wc -l
3. 输出到文件
# 导出类列表
sc * > /tmp/classes.txt
# 导出线程信息
thread > /tmp/threads.txt
# 导出反编译代码
jad com.demoCode.edu.backend.controller.AnalysisTaskController > /tmp/Controller.java
5.2 批处理脚本
创建Arthas脚本文件
# script.as
dashboard -n 1
thread -n 5
jvm
执行脚本
java -jar arthas-boot.jar -c "source /path/to/script.as"
5.3 异步任务处理
后台异步执行
# 使用&在后台执行
trace com.demoCode.edu.backend.service.AnalysisTaskService processData &
# 查看后台任务
jobs
# 停止后台任务
kill <job-id>
5.4 OGNL高级用法
1. 调用Spring Bean方法
# 获取Spring上下文
ognl '#context=@org.springframework.web.context.support.WebApplicationContextUtils@getWebApplicationContext(@javax.servlet.http.HttpServlet@getServletContext())'
# 简化方式:如果项目中有静态的ApplicationContext
ognl '@com.demoCode.edu.backend.DataQualityMonitorApplication@applicationContext'
2. 修改Bean属性(谨慎使用)
# 获取配置Bean
ognl '#config=#springContext.getBean("systemConfig")'
# 查看属性
ognl '#config.getMaxUploadSize()'
# 修改属性(仅在内存中,重启后失效)
ognl '#config.setMaxUploadSize(1048576000)'
3. 执行复杂表达式
# 遍历列表
ognl '#list=@java.util.Arrays@asList(1,2,3,4,5), #sum=0, #list.{#sum+=#this}, #sum'
# 过滤集合
ognl '#springContext.getBean("cacheManager").getCacheNames().{? #this.startsWith("data")}'
5.5 性能分析最佳实践
1. 分层追踪
# 第一层:Controller
trace com.demoCode.edu.backend.controller.AnalysisTaskController analyze
# 发现Service慢,追踪Service
trace com.demoCode.edu.backend.service.AnalysisTaskService processData
# 发现某个工具方法慢,继续追踪
trace com.demoCode.edu.backend.util.DataUtil process
2. 组合使用
# 同时监控和追踪
monitor -c 5 com.demoCode.edu.backend.controller.AnalysisTaskController analyze &
trace com.demoCode.edu.backend.controller.AnalysisTaskController analyze &
3. 条件过滤
# 只追踪慢请求
trace com.demoCode.edu.backend.controller.AnalysisTaskController analyze '#cost > 1000'
# 只追踪特定参数的请求
watch com.demoCode.edu.backend.controller.AnalysisTaskController analyze "{params}" "params[0].taskId==123" -x 2
六、实战案例
案例1:定位慢SQL问题
问题描述: 数据分析接口响应慢,怀疑是数据库查询慢
解决过程:
# 1. 追踪Controller方法
[arthas@12345]$ trace com.demoCode.edu.backend.controller.AnalysisTaskController analyze
`---[3456.8ms] analyze()
`---[3450.2ms] analysisTaskService.analyze()
# 2. 继续追踪Service
[arthas@12345]$ trace com.demoCode.edu.backend.service.AnalysisTaskService analyze
`---[3450.2ms] analyze()
+---[5.6ms] validateRequest()
+---[3440.5ms] analysisTaskMapper.selectAnalysisData() <--- 发现Mapper方法慢
`---[4.1ms] buildResult()
# 3. 监控Mapper方法,查看SQL参数
[arthas@12345]$ watch com.demoCode.edu.backend.mapper.AnalysisTaskMapper selectAnalysisData "{params,returnObj,#cost}" -x 3
result=@ArrayList[
@Object[][
@AnalysisQuery[
startDate=2020-01-01, <--- 发现查询范围太大
endDate=2024-01-15,
limit=null <--- 没有分页限制
],
],
@List[size=1234567], <--- 返回数据量巨大
@Long[3440]
]
# 4. 反编译查看Mapper.xml或方法
[arthas@12345]$ jad com.demoCode.edu.backend.mapper.AnalysisTaskMapper
# 发现SQL没有加索引,且查询范围过大
解决方案:
- 添加日期索引
- 限制查询范围
- 添加分页
- 优化SQL
案例2:Spring Bean循环依赖问题
问题描述: 应用启动失败,提示循环依赖
解决过程:
# 1. 启动Arthas(需要在应用启动后立即attach)
java -jar arthas-boot.jar
# 2. 查看Spring容器状态
[arthas@12345]$ ognl '@org.springframework.boot.SpringApplication@run'
# 3. 查看Bean定义
[arthas@12345]$ ognl '#springContext.getBeanDefinitionNames()'
# 4. 查看具体Bean的依赖
[arthas@12345]$ ognl '#springContext.getBean("orderService").getClass().getDeclaredFields().{name}'
# 发现OrderService依赖InventoryService
# InventoryService也依赖OrderService
# 5. 查看注入方式
[arthas@12345]$ jad com.demoCode.edu.backend.service.OrderService
@Service
public class OrderService {
@Autowired
private InventoryService inventoryService; <--- 字段注入
...
}
解决方案:
- 改为构造器注入
- 使用@Lazy延迟加载
- 重新设计类结构,解除循环依赖
案例3:线程池任务堆积
问题描述: 系统变慢,线程池任务处理不过来
解决过程:
# 1. 查看线程状态
[arthas@12345]$ thread | grep pool
pool-1-thread-1 WAITING
pool-1-thread-2 WAITING
pool-1-thread-3 WAITING
...
pool-1-thread-50 WAITING
# 发现大量线程在WAITING状态
# 2. 查看具体在等待什么
[arthas@12345]$ thread <线程ID>
"pool-1-thread-1" Id=50 WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@7f31245a
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
...
# 3. 获取线程池信息
[arthas@12345]$ ognl '#springContext.getBean("asyncTaskExecutor")'
@ThreadPoolTaskExecutor[
corePoolSize=10,
maxPoolSize=50,
queueCapacity=100,
activeCount=50, <--- 线程全部活跃
poolSize=50,
queueSize=5000 <--- 队列堆积了5000个任务
]
# 4. 监控任务提交情况
[arthas@12345]$ watch com.demoCode.edu.backend.service.AsyncTaskService submitTask "{params}" -n 50
# 发现任务提交速度远大于处理速度
解决方案:
- 增加线程池大小
- 增加队列容量
- 优化任务执行时间
- 添加任务拒绝策略
七、最佳实践
7.1 安全建议
1. 生产环境使用注意事项
# ✅ 推荐:使用只读命令
dashboard
thread
jvm
sc
sm
jad
# ⚠️ 谨慎使用
watch # 会有性能开销
trace # 会有性能开销
monitor # 会有性能开销
# ❌ 禁止随意使用
redefine # 可能导致系统不稳定
ognl # 可能修改系统状态
stop # 会关闭Arthas
2. 权限控制
# 限制可以attach的用户
# 只允许运行Java应用的用户使用Arthas
# 使用防火墙限制远程访问
iptables -A INPUT -p tcp --dport 3658 -s <允许的IP> -j ACCEPT
iptables -A INPUT -p tcp --dport 3658 -j DROP
3. 审计日志
# Arthas操作会记录在用户目录下
~/.arthas/log/
# 定期检查日志
tail -f ~/.arthas/log/arthas.log
7.2 性能影响
各命令的性能开销:
| 命令 | 性能影响 | 说明 |
|---|---|---|
| dashboard | 很小 | 只是读取JVM信息 |
| thread | 很小 | 只是读取线程信息 |
| jvm | 很小 | 只是读取JVM信息 |
| sc/sm/jad | 小 | 类加载器遍历,影响小 |
| watch | 中 | 每次方法调用都会触发,高频方法影响大 |
| trace | 中 | 追踪每个方法调用,影响较大 |
| monitor | 小-中 | 定期统计,影响相对小 |
| tt | 大 | 需要保存现场,内存和CPU开销都大 |
| profiler | 中 | 采样式分析,影响可控 |
建议:
- 问题排查完毕后立即stop Arthas
- 避免在高峰期使用trace/watch
- 使用-n参数限制执行次数
- 使用条件表达式减少触发频率
7.3 常用命令组合
场景1:快速诊断
# 一键查看系统状态
dashboard -n 1 && thread -n 3 && jvm
场景2:接口慢问题
# 1. 追踪调用链路
trace com.example.Controller method -n 10
# 2. 查看方法参数和耗时
watch com.example.Service method "{params,#cost}" -x 2 -n 10
# 3. 生成火焰图
profiler start && sleep 30 && profiler stop
场景3:验证代码部署
# 1. 查看类信息
sc -d com.example.Controller
# 2. 反编译查看源码
jad com.example.Controller
# 3. 查看方法
sm com.example.Controller method
7.4 问题排查思路
标准排查流程:
1. 确认问题
├─ dashboard: 查看系统整体状态
├─ thread: 查看线程状态
└─ jvm: 查看JVM信息
2. 定位问题位置
├─ trace: 追踪方法调用链路
├─ watch: 查看方法参数和返回值
└─ stack: 查看方法调用栈
3. 分析根本原因
├─ jad: 查看实际运行的代码
├─ sc/sm: 确认类和方法是否正确
└─ profiler: 生成火焰图分析性能
4. 验证解决方案
├─ watch: 验证修改后的效果
└─ monitor: 持续监控
八、常见问题FAQ
Q1: Arthas启动失败,提示无法attach
A:
# 检查Java进程是否存在
jps -l
# 检查是否有权限
# Arthas需要和目标进程是同一用户
# 如果是容器环境,需要特殊配置
# Docker需要添加参数:--cap-add=SYS_PTRACE
# 检查JVM参数,确保没有禁用attach
# 不要设置 -XX:+DisableAttachMechanism
Q2: 找不到类或方法
A:
# 1. 确认类名完整路径
sc *ClassName
# 2. 确认类已经被加载
# 如果类还没被使用过,可能没有加载
# 可以先触发一次相关功能
# 3. 检查ClassLoader
sc -d com.example.Class
# 查看 classLoaderHash,确认是否加载了多个版本
Q3: watch命令没有输出
A:
# 1. 确认方法是否被调用
# 发起一次请求,看是否有输出
# 2. 检查表达式是否正确
# 使用简单表达式测试
watch com.example.Class method "{params}"
# 3. 检查条件表达式
# 条件可能过滤掉了所有结果
# 4. 增加展开层级
watch com.example.Class method "{params}" -x 3
# 5. 使用-b参数在方法调用前观察
watch com.example.Class method "{params}" -b
Q4: redefine失败
A:
# redefine有限制:
# ❌ 不能添加字段
# ❌ 不能删除字段
# ❌ 不能添加方法
# ❌ 不能删除方法
# ✅ 只能修改方法体
# 解决方案:
# 1. 如果需要添加字段/方法,只能重启应用
# 2. 确保.class文件是正确编译的
# 3. 检查类的路径是否正确
Q5: Arthas占用过多内存
A:
# 1. 停止不需要的监控命令
jobs # 查看后台任务
kill <job-id> # 停止任务
# 2. 清理tt记录
tt -d # 删除所有记录
# 3. 限制命令执行次数
watch com.example.Class method "{params}" -n 100
# 4. 使用完毕后退出
stop
Q6: 如何在Docker容器中使用Arthas
A:
# 1. 进入容器
docker exec -it <container-id> /bin/bash
# 2. 安装Arthas(如果容器中没有curl,需要先安装)
curl -O https://arthas.aliyun.com/arthas-boot.jar
# 3. 启动
java -jar arthas-boot.jar
# 或者在Dockerfile中预装Arthas
FROM openjdk:8
# ... 其他配置
RUN curl -O https://arthas.aliyun.com/arthas-boot.jar
Q7: trace命令输出不完整
A:
# 1. 增加trace深度
trace com.example.Class method --skipJDKMethod false
# 2. 可能是方法内部有异常
# 使用-e参数观察异常
trace com.example.Class method -e
# 3. 可能是异步调用
# trace无法追踪异步方法
# 需要分别trace异步方法
Q8: 如何保存Arthas输出结果
A:
# 方法1: 重定向到文件
jad com.example.Class > /tmp/Class.java
thread > /tmp/threads.txt
# 方法2: 使用tee命令(可以同时显示和保存)
jad com.example.Class | tee /tmp/Class.java
# 方法3: Arthas自带的日志
# 所有命令都会记录在
~/.arthas/log/arthas.log
总结
Arthas核心价值
- 快速定位问题:无需重启、无需加日志
- 实时监控:watch/trace/monitor实时查看方法调用
- 热修复:紧急情况下可以redefine修复bug
- 性能分析:火焰图快速找出性能瓶颈
- 提高效率:大幅减少问题排查时间
常用命令速查表
| 场景 | 命令 |
|---|---|
| 查看系统状态 | dashboard |
| 查看CPU高的线程 | thread -n 3 |
| 查找类 | sc *ClassName |
| 反编译 | jad com.example.Class |
| 观察方法 | watch com.example.Class method "{params,returnObj}" |
| 追踪调用链路 | trace com.example.Class method |
| 方法调用栈 | stack com.example.Class method |
| 生成火焰图 | profiler start → profiler stop |
| 查找死锁 | thread -b |
| 热更新 | redefine /path/to/Class.class |
学习建议
- 从简单命令开始:dashboard → thread → jvm
- 多实践:在测试环境多尝试各种命令
- 结合实际问题:遇到问题时尝试用Arthas解决
- 阅读官方文档:https://arthas.aliyun.com/doc/
- 加入社区:GitHub Issues、Stack Overflow
最后的提醒
⚠️ 生产环境使用Arthas的注意事项:
- 谨慎使用会影响性能的命令(watch、trace、tt)
- 使用完毕后及时stop
- 不要随意使用redefine
- 保存好操作日志,便于后续审计
- 了解公司的运维规范
Arthas是Java开发者必备的诊断工具,熟练掌握后能大大提高问题排查效率!
参考资料:
- Arthas官方文档:https://arthas.aliyun.com/doc/
- Arthas GitHub:https://github.com/alibaba/arthas
- 阿里云Arthas教程:https://arthas.aliyun.com/doc/arthas-tutorials.html
2121

被折叠的 条评论
为什么被折叠?



