Arthas线上问题排查这篇文章就够啦

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没有加索引,且查询范围过大

解决方案:

  1. 添加日期索引
  2. 限制查询范围
  3. 添加分页
  4. 优化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;  <--- 字段注入
  
    ...
}

解决方案:

  1. 改为构造器注入
  2. 使用@Lazy延迟加载
  3. 重新设计类结构,解除循环依赖

案例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

# 发现任务提交速度远大于处理速度

解决方案:

  1. 增加线程池大小
  2. 增加队列容量
  3. 优化任务执行时间
  4. 添加任务拒绝策略

七、最佳实践

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核心价值

  1. 快速定位问题:无需重启、无需加日志
  2. 实时监控:watch/trace/monitor实时查看方法调用
  3. 热修复:紧急情况下可以redefine修复bug
  4. 性能分析:火焰图快速找出性能瓶颈
  5. 提高效率:大幅减少问题排查时间

常用命令速查表

场景命令
查看系统状态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 startprofiler stop
查找死锁thread -b
热更新redefine /path/to/Class.class

学习建议

  1. 从简单命令开始:dashboard → thread → jvm
  2. 多实践:在测试环境多尝试各种命令
  3. 结合实际问题:遇到问题时尝试用Arthas解决
  4. 阅读官方文档:https://arthas.aliyun.com/doc/
  5. 加入社区:GitHub Issues、Stack Overflow

最后的提醒

⚠️ 生产环境使用Arthas的注意事项:

  1. 谨慎使用会影响性能的命令(watch、trace、tt)
  2. 使用完毕后及时stop
  3. 不要随意使用redefine
  4. 保存好操作日志,便于后续审计
  5. 了解公司的运维规范

Arthas是Java开发者必备的诊断工具,熟练掌握后能大大提高问题排查效率!


参考资料:

  • Arthas官方文档:https://arthas.aliyun.com/doc/
  • Arthas GitHub:https://github.com/alibaba/arthas
  • 阿里云Arthas教程:https://arthas.aliyun.com/doc/arthas-tutorials.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值