Arthas单独记录篇二——命令

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.6k人参与

Arthas安装

1. 在线安装(推荐)

使用官方安装脚本:

# 使用curl curl -O https://arthas.aliyun.com/arthas-boot.jar

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

启动Arthas:

java -jar arthas-boot.jar

2. 包管理器安装

使用Homebrew(macOS):

brew install arthas

使用SDKMAN:

sdk install arthas

3. 离线安装

下载完整包:

# 从GitHub Releases下载
wget https://github.com/alibaba/arthas/releases/download/arthas-all-3.7.1/arthas-bin.zip
unzip arthas-bin.zip
cd arthas
./as.sh

4. Docker安装

使用Docker运行:

docker run --rm -it hengyunabc/arthas

附加到运行中的容器:

docker exec -it <container_name> bash java -jar arthas-boot.jar

5. 通过Maven/Gradle依赖安装

Maven项目:

<dependency>
    <groupId>com.taobao.arthas</groupId>
    <artifactId>arthas-spring-boot-starter</artifactId>
    <version>3.7.1</version>
</dependency>

Gradle项目:

implementation 'com.taobao.arthas:arthas-spring-boot-starter:3.7.1'

6. 安装验证

检查版本:

java -jar arthas-boot.jar --version

查看帮助:

java -jar arthas-boot.jar -h

7. 安装选项参数

指定目标进程:

# 连接到指定PID的进程

java -jar arthas-boot.jar --target-ip 127.0.0.1 --telnet-port 3658 --http-port 8563

# 列出所有Java进程

java -jar arthas-boot.jar --list

静默安装:

# 无交互式安装 echo "1" | java -jar arthas-boot.jar

8. 安装目录结构

成功安装后,目录结构如下:

arthas/
├── arthas-boot.jar      # 启动器
├── arthas-core.jar      # 核心库
├── arthas-agent.jar     # 代理
├── as.sh                # Linux启动脚本
├── as.bat               # Windows启动脚本
└── lib/                 # 依赖库

9. 常见安装问题解决

权限问题:

# 添加执行权限 chmod +x as.sh chmod +x arthas-boot.jar

网络问题:

# 使用国内镜像

wget https://maven.aliyun.com/repository/public/com/taobao/arthas/arthas-packaging/3.7.1/arthas-packaging-3.7.1-bin.zip

Java版本兼容:

  • 支持Java 6+
  • 推荐Java 8+

10. 卸载方法

清理安装文件:

rm -rf ~/.arthas/ rm -f arthas-boot.jar

停止Arthas服务:

# 在Arthas控制台中执行 stop

安装建议

  1. 生产环境‌:推荐使用离线安装,避免网络依赖
  2. 开发环境‌:使用在线安装,方便更新
  3. 容器环境‌:使用Docker镜像或挂载方式
  4. 权限管理‌:确保有足够的权限附加到目标进程

以上是Arthas的完整安装方法和相关配置说明。

Arthas启动

1. 基础启动命令

启动并选择目标进程:

java -jar arthas-boot.jar

启动后会列出所有Java进程,输入数字选择要诊断的进程。

指定PID直接连接:

java -jar arthas-boot.jar [pid]

2. 启动参数详解

网络配置:

# 指定IP和端口

java -jar arthas-boot.jar --target-ip 127.0.0.1 --telnet-port 3658 --http-port 8563

# 仅使用HTTP接口(禁用Telnet)

java -jar arthas-boot.jar --telnet-port -1 --http-port 8563

会话管理:

# 使用指定会话ID java -jar arthas-boot.jar --session-id my-session

# 设置会话超时时间 java -jar arthas-boot.jar --session-timeout 1800

3. 高级启动选项

日志配置:

# 指定日志输出级别 java -jar arthas-boot.jar --log-level debug

# 指定日志文件路径 java -jar arthas-boot.jar --log-file /path/to/arthas.log

性能调优:

# 设置命令执行超时时间 java -jar arthas-boot.jar --command-timeout 300

# 限制命令执行结果大小 java -jar arthas-boot.jar --result-limit 1048576

4. 批量操作模式

非交互式启动:

# 通过管道自动选择第一个进程

echo "1" | java -jar arthas-boot.jar

# 执行特定命令后退出

java -jar arthas-boot.jar --batch-command "dashboard -n 1" --pid [pid]

脚本执行:

# 执行脚本文件

java -jar arthas-boot.jar --batch-file /path/to/script.txt --pid [pid]

5. 连接模式

附加到远程进程:

# 连接远程JVM java -jar arthas-boot.jar --target-ip 192.168.1.100 --pid [pid]

隧道连接:

# 通过隧道服务器连接

java -jar arthas-boot.jar --tunnel-server ws://server:8080 --app-name myapp

6. 诊断模式

安全模式:

# 只读模式,防止误操作 java -jar arthas-boot.jar --safe-mode

统计模式:

# 启用命令执行统计 java -jar arthas-boot.jar --stat-enabled

7. 常用组合命令

快速诊断:

# 启动并立即执行dashboard echo -e "1\ndashboard" | java -jar arthas-boot.jar

生产环境启动:

java -jar arthas-boot.jar \
  --target-ip 127.0.0.1 \
  --telnet-port 3658 \
  --http-port 8563 \
  --log-level info \
  --session-timeout 3600

8. 进程选择技巧

列出所有Java进程:

java -jar arthas-boot.jar --list

过滤特定进程:

# 根据进程名过滤 java -jar arthas-boot.jar --select com.example.Application

9. 启动问题排查

检查连接状态:

# 验证是否能连接到目标进程 java -jar arthas-boot.jar --test-connection --pid [pid]

查看启动帮助:

java -jar arthas-boot.jar --help

10. 实际使用示例

开发环境:

# 快速启动,选择第一个进程 java -jar arthas-boot.jar

测试环境:

# 指定具体应用进程 ps aux | grep java java -jar arthas-boot.jar [具体的pid]

生产环境:

# 完整的生产配置
java -jar arthas-boot.jar \
  --pid [生产进程PID] \
  --target-ip 127.0.0.1 \
  --telnet-port 3658 \
  --http-port 8563 \
  --log-level warn \
  --session-timeout 1800 \
  --command-timeout 60

这些启动命令覆盖了Arthas的各种使用场景,可以根据具体需求选择合适的启动方式和参数配置。

dashboard命令基本语法

dashboard [选项参数]

1. 基本用法

启动仪表板:

dashboard

按指定时间间隔刷新:

# 每3秒刷新一次 dashboard -i 3

# 每5秒刷新一次 dashboard -i 5

指定刷新次数后退出:

# 刷新10次后自动退出 dashboard -n 10

2. 输出界面详解

dashboard命令会显示一个实时监控面板,包含多个监控区域:

顶部汇总信息

ID   NAME                          GROUP        PRIORITY STATE  %CPU   TIME   INTERRUPTED DAEMON
1    main                          main         5       RUNNABLE 15.3% 0:05.3 false      false
12   http-nio-8080-exec-1          main         5       RUNNABLE 32.1% 1:23.1 true       true

内存监控区域

Memory                    used      total      max        usage
heap                      128M      512M       2G         25%
nonheap                   45M       64M        256M       17%

线程监控区域

Runtime
os.name                         Linux
os.version                      4.19.0
java.version                    1.8.0_291
java.home                       /usr/lib/jvm/java-8-openjdk
systemload.average             0.12
processors                      4
uptime                          2d 3h 45m

GC监控区域

Garbage Collection
PS Scavenge        count/time         125 / 3.2s
PS MarkSweep       count/time         8 / 1.5s

3. 常用选项参数

-i 设置刷新间隔

# 设置2秒刷新间隔 dashboard -i 2

-n 设置刷新次数

# 只刷新5次后退出 dashboard -n 5

组合使用

# 每2秒刷新一次,共刷新20次 dashboard -i 2 -n 20

4. 监控指标说明

CPU使用率

  • 显示每个线程的CPU占用百分比
  • 可快速定位CPU热点线程

内存使用情况

  • heap‌:堆内存使用情况
  • nonheap‌:非堆内存使用情况
  • usage‌:内存使用率百分比

线程状态

  • RUNNABLE‌:运行中或等待CPU
  • BLOCKED‌:阻塞状态
  • WAITING‌:等待状态
  • TIMED_WAITING‌:限时等待

垃圾收集

  • 显示各GC收集器的收集次数和耗时
  • 帮助分析GC性能问题

5. 交互操作

刷新控制

  • 自动按设定间隔刷新
  • 可随时按qCtrl+C退出

线程排序

  • 默认按CPU使用率排序
  • 可查看线程ID、名称、状态等详细信息

6. 实际应用场景

实时性能监控

# 持续监控系统状态 dashboard -i 3

问题排查

# 监控一段时间内的系统表现 dashboard -i 2 -n 30

内存泄漏分析

通过持续观察内存使用趋势,识别内存泄漏问题

7. 使用技巧

  1. 快速概览‌:直接运行dashboard获取系统整体状态
  2. 持续监控‌:使用-i参数设置合适的刷新频率
  3. 自动退出‌:使用-n参数避免手动中断
  4. 结合分析‌:发现异常后使用threadjvm等命令深入分析

dashboard命令提供了一个全面的实时监控界面,能够帮助开发人员快速了解Java应用的运行状态,识别性能瓶颈和异常情况。

thread命令基本语法

thread [选项参数] [线程ID] [条件表达式]

1. 基本用法

查看所有线程:

thread

查看指定线程:

thread 1 # 查看线程ID为1的线程

thread 1,2,3 # 查看多个指定线程

2. 常用选项参数

-n 显示最忙的线程

# 显示CPU占用率最高的3个线程 thread -n 3

-b 检测死锁

# 查找死锁信息 thread -b

-i 设置采样间隔

# 每1000毫秒采样一次 thread -i 1000

--state 按状态过滤

# 查看RUNNABLE状态的线程 thread --state RUNNABLE

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

# 查看WAITING状态的线程 thread --state WAITING

3. 输出结果详解

标准thread命令输出:

Threads Total: 45, NEW: 0, RUNNABLE: 12, BLOCKED: 2, WAITING: 25, TIMED_WAITING: 6, TERMINATED: 0
ID    NAME                           GROUP        PRIORI STATE   %CPU    TIME   INTER DAEMON
1     main                           main         5      RUNNABL 15.3%   0:05.3 false false
12    http-nio-8080-exec-1          main         5      RUNNABL 32.1%   1:23.1 true  true
23    Thread-0                       main         5      WAITING 0.0%    0:00.1 false false

输出字段说明:

  • ID:线程ID
  • NAME:线程名称
  • GROUP:线程组
  • PRIORI:优先级
  • STATE:线程状态
  • %CPU:CPU使用率
  • TIME:运行时间
  • INTER:是否中断
  • DAEMON:是否为守护线程

thread -n 3 输出:

Threads Total: 45, NEW: 0, RUNNABLE: 12, BLOCKED: 2, WAITING: 25, TIMED_WAITING: 6, TERMINATED: 0
ID    NAME                STATE     %CPU    TIME  
12    http-nio-8080-exec-1 RUNNABLE  98.5%   5:34.2
13    http-nio-8080-exec-2 RUNNABLE  45.2%   3:12.8
15    pool-1-thread-1    RUNNABLE  12.3%   1:45.6

thread -b 死锁检测输出:

Blocked thread: "Thread-1" Id=23 TIMED_WAITING
    at java.lang.Thread.sleep(Native Method)
    at com.example.DeadLockDemo.lambda$main$1(DeadLockDemo.java:25)
    - locked <0x0000000712345678> (a java.lang.Object)
    at com.example.DeadLockDemo$$Lambda$2/0x0000000800b8e440.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

Blocked thread: "Thread-2" Id=24 BLOCKED
    at com.example.DeadLockDemo.lambda$main$2(DeadLockDemo.java:35)
    - waiting to lock <0x0000000712345678> (a java.lang.Object)
    at com.example.DeadLockDemo$$Lambda$3/0x0000000800b8e840.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

4. 线程状态说明

  • NEW‌:新建状态,尚未启动
  • RUNNABLE‌:可运行状态,正在执行或等待CPU
  • BLOCKED‌:阻塞状态,等待获取监视器锁
  • WAITING‌:等待状态,无限期等待其他线程操作
  • TIMED_WAITING‌:限时等待状态
  • TERMINATED‌:终止状态

5. 实际应用场景

性能问题排查:

# 找到CPU占用最高的线程 thread -n 3

# 查看该线程的堆栈信息 thread 12

死锁检测:

# 快速检测死锁 thread -b

线程池监控:

# 监控线程池中线程的状态 thread --state RUNNABLE -n 5

6. 高级用法

条件表达式过滤:

# 只查看CPU使用率超过50%的线程 thread 'percent > 50'

# 查看特定名称模式的线程 thread 'name ~= "http.*"'

持续监控:

# 每2秒采样一次,持续监控 thread -i 2000

7. 实用技巧

  1. 快速定位‌:先用thread -n 3找到热点线程
  2. 深入分析‌:再用thread [线程ID]查看详细堆栈
  3. 状态分析‌:关注BLOCKED和WAITING状态的线程
  4. 结合使用‌:配合stack命令查看完整调用链路

thread命令是排查Java应用性能问题的基础工具,通过分析线程状态和CPU使用情况,可以快速定位死锁、线程阻塞、CPU过高等问题。

monitor命令基本语法

monitor 类全限定名 方法名 [周期] [条件表达式]

1. 基本用法

监控单个方法:

# 监控指定方法,默认5秒输出一次统计 monitor com.example.UserService getUserById

# 指定3秒输出一次统计 monitor com.example.UserService getUserById -c 3

监控多个方法:

# 监控类中所有方法 monitor com.example.UserService *

# 监控包下所有类的指定方法 monitor com.example.service.* * -c 5

2. 输出结果详解

monitor命令的典型输出格式:

timestamp | class | method | total | success | fail | avg-ms(rate)
2023-10-01 14:30:00 | com.example.UserService | getUserById | 125 | 120 | 5 | 45.2(96.0%) 
2023-10-01 14:30:05 | com.example.UserService | getUserById | 142 | 142 | 0 | 38.7(100.0%) 
2023-10-01 14:30:10 | com.example.UserService | getUserById | 118 | 118 | 0 | 42.1(100.0%)

输出字段说明:

  • timestamp:统计时间点
  • class:监控的类名
  • method:监控的方法名
  • total:总调用次数
  • success:成功调用次数
  • fail:失败调用次数
  • avg-ms:平均执行时间(毫秒)
  • rate:成功率百分比

3. 常用选项参数

-c 统计周期

# 每2秒统计一次 monitor com.example.UserService getUserById -c 2

# 每10秒统计一次 monitor com.example.UserService getUserById -c 10

条件表达式过滤

# 只统计参数大于1000的调用

monitor com.example.UserService getUserById 'params[0] > 1000'

# 只统计执行时间超过50ms的调用

monitor com.example.UserService getUserById '#cost > 50'

4. 实际应用场景

性能监控:

# 监控关键业务方法性能

monitor com.example.OrderService createOrder -c 5

monitor com.example.PaymentService processPayment -c 5

异常监控:

# 监控方法异常率 monitor com.example.FileService uploadFile -c 10

负载监控:

# 监控QPS和响应时间 monitor com.example.ApiController * -c 3

5. 高级用法

监控构造方法:

# 监控对象创建频率 monitor com.example.UserService <init> -c 5

监控静态方法:

# 监控工具类方法 monitor com.example.Utils * -c 10

组合监控:

# 同时监控多个服务类 monitor com.example.*Service * -c 5

6. 输出结果解读示例

假设监控一个用户查询服务:

分析要点:

  • 调用量趋势‌:从156→178→162,相对稳定
  • 成功率‌:96.2%→100%→98.8%,需要关注失败情况
  • 性能表现‌:平均52ms左右,性能稳定
  • 异常情况‌:有少量失败调用,需要进一步排查

7. 实用技巧

  1. 合理设置周期‌:根据业务特点设置监控周期,高频方法周期短些
  2. 关注失败率‌:fail列不为0时需要注意
  3. 性能基准‌:建立方法的性能基准,avg-ms异常时及时排查
  4. 组合使用‌:配合watch和trace命令进行深度分析

8. 退出监控

# 按Ctrl+C或Q键退出监控

Press Q or Ctrl+C to abort.

monitor命令非常适合用于长期监控方法的性能指标和健康状况,通过周期性的统计输出,可以快速发现性能瓶颈和异常情况。

trace命令基本语法

trace 类全限定名 方法名 [条件表达式] [选项参数]

1. 基本用法

追踪单个方法:

# 追踪指定方法的调用链路 trace com.example.UserService getUserById

# 追踪静态方法 trace com.example.Utils formatDate

追踪多个方法:

# 追踪类中所有方法 trace com.example.UserService *

# 追踪包下所有类的指定方法 trace com.example.service.* getUser*

2. 输出结果详解

trace命令的典型输出格式:

`---ts=2023-10-01 15:30:25;thread_name=http-nio-8080-exec-1;id=23;is_daemon=true;priority=5;TCCL=org.springframework.boot.loader.LaunchedURLClassLoader@123456
    `---[12.456ms] com.example.UserService:getUserById()
        +---[1.234ms] com.example.UserService:validateInput() #16
        +---[8.901ms] com.example.UserMapper:selectById() #17
        |   `---[8.567ms] org.apache.ibatis.*:execute() #18
        `---[2.321ms] com.example.UserService:formatResponse() #19
 

输出字段说明:

  • 显示完整的方法调用链
  • ts:时间戳
  • thread_name:线程名称
  • id:线程ID
  • #cost:方法执行时间(毫秒)
  • #行号:源代码行号标识

3. 常用选项参数

-j 跳过jdk方法

# 跳过JDK内部方法调用,只显示业务代码

trace com.example.UserService getUserById -j

-n 执行次数限制

# 只追踪3次调用 trace com.example.UserService getUserById -n 3

--skipJDKMethod false 包含JDK方法

# 显示包括JDK方法在内的完整调用链

trace com.example.UserService getUserById --skipJDKMethod false

-E 正则表达式匹配

# 使用正则表达式匹配方法名 trace -E com\.example\.service\..* getUser.*

4. 条件表达式过滤

基于执行时间:

# 只追踪执行时间超过50ms的调用

trace com.example.UserService getUserById '#cost > 50'

基于方法参数:

# 只追踪特定参数的调用

trace com.example.UserService getUserById 'params[0] > 1000'

组合条件:

# 组合多个条件

trace com.example.UserService getUserById 'params[0] && #cost > 100'

5. 实际应用场景

性能瓶颈定位:

# 追踪慢查询方法 trace com.example.DataService queryLargeData '#cost > 1000'

调用链路分析:

# 分析完整业务链路 trace com.example.OrderService createOrder

异常调用追踪:

# 追踪特定条件下的调用

trace com.example.PaymentService processPayment 'params[1].amount > 10000'

6. 高级用法

追踪构造方法:

# 追踪对象创建过程 trace com.example.UserService <init>

追踪接口实现:

# 追踪接口的所有实现方法 trace com.example.*RepositoryImpl *

深度追踪:

# 追踪多层调用,显示完整调用树

trace com.example.Controller * --skipJDKMethod false

7. 实用技巧

  1. 精确匹配‌:使用完整类名和方法名避免误匹配
  2. 条件过滤‌:使用条件表达式减少干扰信息
  3. 结合使用‌:先使用monitor找到热点方法,再用trace深入分析
  4. 性能考虑‌:在生产环境谨慎使用,避免影响应用性能

8. 退出追踪

# 按Ctrl+C或Q键退出追踪

Press Q or Ctrl+C to abort.

9. 典型排查流程

# 1. 找到高CPU线程 thread -n 3

# 2. 分析线程堆栈 thread 23

# 3. 追踪热点方法 trace com.example.ExpensiveService calculate

# 4. 分析具体耗时环节 trace com.example.ExpensiveService calculate -j

trace命令是性能分析的重要工具,通过观察方法调用链路和执行时间,可以快速定位性能瓶颈所在的具体代码位置。

watch命令基本语法

watch 类全限定名 方法名 观察表达式 [条件表达式] [选项参数]

1. 观察表达式详解

基本观察对象:

# 观察参数 watch com.example.UserService getUserById params

# 观察返回值 watch com.example.UserService getUserById returnObj

# 观察执行时间 watch com.example.UserService getUserById '#cost'

# 观察目标对象 watch com.example.UserService getUserById target

# 观察方法名 watch com.example.UserService getUserById method

组合观察表达式:

# 观察多个元素

watch com.example.UserService getUserById '{params, returnObj, #cost}'

# 观察异常信息

watch com.example.UserService getUserById '{params, throwExp, #cost}'

# 观察完整上下文

watch com.example.UserService getUserById '{params, target, returnObj, #cost}'

2. 条件表达式过滤

# 只观察执行时间超过100ms的调用

watch com.example.UserService getUserById '{params, returnObj}' '#cost > 100'

# 只观察特定参数的调用

watch com.example.UserService getUserById returnObj 'params[0] > 1000'

# 只观察返回特定结果的调用

watch com.example.UserService getUserById returnObj 'returnObj != null'

# 组合条件

watch com.example.UserService getUserById '{params, #cost}' 'params[0] && #cost > 50'

3. 常用选项参数

-x 深度展开

# 展开1层(默认)

watch com.example.UserService getUserById returnObj -x 1

# 展开2层

watch com.example.UserService getUserById returnObj -x 2

# 展开3层(推荐用于复杂对象)

watch com.example.UserService getUserById returnObj -x 3

-b 观察方法调用前

# 方法调用前观察参数 watch com.example.UserService getUserById params -b

# 方法调用前观察目标对象状态 watch com.example.UserService getUserById target -b

-e 观察方法异常时

# 只在抛出异常时观察

watch com.example.UserService getUserById throwExp -e

# 异常时观察完整上下文

watch com.example.UserService getUserById '{params, throwExp, target}' -e

-s 观察方法调用成功后

# 方法成功返回后观察 watch com.example.UserService getUserById returnObj -s

-f 观察方法调用结束后

# 方法结束后观察(默认,包含正常返回和异常) watch com.example.UserService getUserById returnObj -f

4. 实际应用示例

调试参数传递:

watch com.example.OrderService createOrder params -x 3 -b

性能问题排查:

watch com.example.DataService processData '{params, #cost}' '#cost > 500' -s

数据一致性问题:

watch com.example.AccountService transfer '{params[0], params[1], target.balance}' -x 2

异常排查:

watch com.example.FileService uploadFile '{params, throwExp}' -e

5. 高级用法

观察静态方法:

watch com.example.Utils * '{params, returnObj}' -s

观察重载方法:

# 观察所有同名方法 watch com.example.Converter convert* '{params, returnObj, #cost}'

观察构造方法:

watch com.example.UserService <init> '{params, target}' -b

6. 输出结果解读

典型watch输出:


输出字段说明:

  • ts:时间戳
  • cost:执行耗时(毫秒)
  • result:观察表达式的结果数组
  • @类型:对象的类型标识

输出特点:

  • 显示方法调用的详细上下文
  • 可以查看参数值(params)
  • 可以查看返回值(returnObj)
  • 显示执行耗时(#cost)
  • 支持深度展开(-x参数控制展开层级)

7. 实用技巧

  1. 精确匹配方法‌:使用完整方法签名避免重载方法干扰
  2. 合理设置深度‌:-x 2或3通常足够,避免性能问题
  3. 条件过滤‌:使用条件表达式减少不必要输出
  4. 组合使用‌:配合trace命令进行深度分析

通过灵活运用watch命令的各种选项和表达式,可以深入观察Java方法的执行细节,有效定位各种运行时问题。

Arthas的trace、monitor、watch三个命令都是性能诊断工具三者的主要区别

命令输出频率主要用途数据详细程度
trace每次调用分析调用链路中等
monitor周期性统计性能指标宏观
watch每次调用观察具体数据详细

trace适合分析代码执行路径,monitor适合监控方法性能趋势,watch适合调试具体的数据问题。

stack命令的详细用法

基本语法

stack [option] class-pattern method-pattern [condition-express]

常用参数详解

1. 基础用法

# 追踪指定方法的调用路径 stack com.example.UserService getUserById

# 追踪所有public方法 stack com.example.UserService *

# 追踪指定包下的所有方法 stack com.example.service.* *

2. 条件过滤参数

# 按调用次数过滤 stack com.example.UserService getUserById '#cost > 100'

# 按参数值过滤 stack com.example.UserService getUserById 'params[0]=="123"'

# 按返回值过滤 stack com.example.UserService getUserById 'returnObj!=null'

3. 执行控制参数

# 限制执行次数 stack com.example.UserService getUserById -n 5

# 设置执行时间间隔(毫秒) stack com.example.UserService getUserById -m 1000

# 忽略JDK内部方法 stack com.example.UserService getUserById --skipJDKMethod true

4. 输出控制参数

# 显示调用深度 stack com.example.UserService getUserById -d 10

# 限制输出行数 stack com.example.UserService getUserById -l 50

# 按耗时排序 stack com.example.UserService getUserById --orderBy cost

实际应用场景

场景1:排查方法调用链路

# 查看某个方法被谁调用 stack com.example.controller.UserController getUserInfo

场景2:性能问题定位

# 追踪耗时较长的方法调用 stack com.example.service.* * '#cost > 500'

场景3:参数传递追踪

# 追踪特定参数值的调用路径

stack com.example.OrderService createOrder 'params[0].userId == 1001'

场景4:异常调用排查

# 追踪返回null的方法调用

stack com.example.DataService queryData 'returnObj == null'

高级用法组合

1. 多条件组合

stack com.example.PaymentService processPayment 'params[0].amount > 1000 && #cost > 1000'

2. 正则表达式匹配

# 使用正则匹配类名和方法名 stack *Service get* '#cost > 200'

3. 线程过滤

# 只追踪指定线程的调用 stack com.example.TaskService execute 'thread.name == "task-thread-1"'

输出结果解读

stack命令输出包含:

  • 调用栈深度‌:显示方法调用的层级关系
  • 耗时信息‌:每个调用的执行时间
  • 参数值‌:方法调用的具体参数
  • 返回值‌:方法的返回结果

使用技巧

  1. 精确匹配‌:尽量使用完整的类名和方法名
  2. 条件优化‌:使用合适的条件表达式减少输出噪音
  3. 深度控制‌:根据实际需要调整调用深度
  4. 性能考虑‌:避免在生产环境频繁使用高频率追踪

注意事项

  • stack命令会显著增加系统开销,生产环境慎用
  • 建议先使用sc命令确认类和方法的存在
  • 结合watch、trace命令进行综合分析

通过合理使用stack命令,可以快速定位方法调用链路中的问题,提高问题排查效率。

memory命令基本语法

memory

命令参数详解

1. 基础内存信息

# 查看内存概览 memory

# 按特定单位显示(默认MB)

memory -k # KB单位

memory -m # MB单位(默认)

memory -g # GB单位

2. 堆内存分析

# 查看堆内存详细信息 memory --heap

# 查看堆内存直方图(前10个类) memory --heap -n 10

# 查看堆内存中存活对象 memory --heap --live

3. 非堆内存分析

# 查看非堆内存信息 memory --non-heap

# 查看元空间使用情况 memory --metaspace

4. 详细内存统计

# 查看内存池详细信息 memory --pools

# 查看垃圾回收器相关信息 memory --gc

输出结果解读

memory命令输出包含以下关键信息:

内存区域:

  • heap‌ - 堆内存(新生代、老年代)
  • non-heap‌ - 非堆内存(方法区、元空间等)
  • code cache‌ - JIT编译代码缓存
  • metaspace‌ - 元空间(类元数据)
  • compressed class space‌ - 压缩类空间

关键指标:

  • init‌ - 初始内存大小
  • used‌ - 已使用内存大小
  • committed‌ - 已提交内存大小
  • max‌ - 最大可用内存大小

实际应用场景

场景1:内存泄漏排查

# 定期执行观察内存增长趋势 memory --heap # 间隔一段时间后再次执行对比

场景2:OOM问题分析

# 查看各内存区域使用率 memory --pools

场景3:内存配置优化

# 查看当前内存配置是否合理 memory

高级用法组合

1. 结合其他命令分析

# 先查看内存概况 memory

# 再查看占用内存较多的对象 heapdump --live /tmp/heap.hprof

2. 定期监控

# 每5秒执行一次,共执行10次 memory -i 5000 -n 10

使用技巧

  1. 趋势分析‌:定期执行memory命令观察内存使用趋势
  2. 对比分析‌:在操作前后分别执行,对比内存变化
  3. 综合分析‌:结合jvm、thread等命令进行综合分析

注意事项

  • memory命令不会对系统性能产生显著影响
  • 结果中的committed通常大于used,这是正常现象
  • 关注used与max的比例,避免接近max值

通过memory命令可以全面了解JVM内存使用情况,为内存优化和问题排查提供重要依据。

jvm命令基本语法

jvm

命令功能详解

1. 运行时信息

  • JVM名称‌:HotSpot、OpenJ9等
  • Java版本‌:JDK版本信息
  • 启动时间‌:JVM启动时间戳
  • 运行时间‌:JVM已运行时长

2. 内存相关信息

  • 堆内存使用‌:当前堆内存使用情况
  • 非堆内存‌:方法区、元空间等使用情况
  • 垃圾回收器‌:使用的GC算法和收集器

3. 系统属性

  • 操作系统信息‌:OS名称、架构、版本
  • 用户信息‌:当前用户、工作目录
  • 文件编码‌:默认字符编码

4. 类加载信息

  • 已加载类数‌:当前加载的类数量
  • 总加载类数‌:JVM生命周期内加载的总类数

实际应用场景

场景1:JVM基本信息确认

# 快速查看JVM版本和运行状态 jvm

场景2:环境配置检查

# 确认运行环境和配置参数 jvm

输出结果解读

jvm命令输出包含以下关键信息:

运行时数据:

  • UPTIME‌ - JVM运行时间
  • START-TIME‌ - 启动时间
  • INPUT-ARGUMENTS‌ - 启动参数

内存相关:

  • HEAP-MEMORY-USED‌ - 堆内存已使用量
  • NO-HEAP-MEMORY-USED‌ - 非堆内存使用量

线程信息:

  • THREAD-COUNT‌ - 活动线程数
  • PEAK-THREAD-COUNT‌ - 峰值线程数

使用技巧

  1. 快速诊断‌:jvm命令是排查问题的第一步
  2. 环境确认‌:用于确认运行环境和配置
  3. 参数检查‌:查看JVM启动参数设置

注意事项

  • jvm命令执行开销很小,可频繁使用
  • 结果中的时间信息基于JVM启动时间
  • 关注内存使用率和线程数量变化趋势

通过jvm命令可以快速了解JVM的基本状态,为后续深入诊断提供基础信息。

heapdump命令基本语法

heapdump [--live] [file]

参数说明

可选参数

  • --live:只转存活对象,减少文件大小
  • file:指定输出文件路径,默认为当前目录下的heapdump.hprof文件

常用用法示例

1. 生成默认堆转储

# 在当前目录生成heapdump.hprof文件 heapdump

2. 指定输出路径

# 生成到指定目录 heapdump /tmp/myheap.hprof

3. 只转储存活对象

# 只转储存活对象,文件更小 heapdump --live

4. 完整参数组合

# 转储存活对象到指定文件 heapdump --live /tmp/live_heap.hprof

文件分析工具

生成的.hprof文件可以使用以下工具分析:

1. Eclipse MAT (Memory Analyzer Tool)

  • 功能强大的堆分析工具
  • 自动检测内存泄漏
  • 提供详细的对象引用链

2. JDK自带的jhat

jhat heapdump.hprof

3. VisualVM

  • 图形化分析工具
  • 直观的对象分布查看

实际应用场景

场景1:内存泄漏分析

# 生成堆转储分析内存泄漏 heapdump /tmp/leak_analysis.hprof

场景2:性能优化

# 生成轻量级堆转储,减少对系统影响 heapdump --live /tmp/performance.hprof

注意事项

  1. 文件大小‌:完整堆转储文件可能很大,注意磁盘空间
  2. 系统影响‌:生成堆转储会暂停JVM,生产环境谨慎使用
  3. 分析时间‌:大文件分析需要较长时间和足够内存
  4. 权限‌:确保有权限写入目标目录

使用技巧

  • 在内存使用高峰时生成转储,更容易发现问题
  • 使用--live参数可以减少文件大小和分析时间
  • 定期生成堆转储进行对比分析
  • 结合其他内存监控命令使用效果更好

heapdump命令是诊断内存问题的关键工具,配合分析工具可以深入定位内存泄漏和优化内存使用。

jad命令基本语法

jad [option] [class-name] [method-name]

参数说明

主要参数

  • class-name:要反编译的类名(全限定名)
  • method-name:可选,指定反编译特定方法
常用选项
  • --source-only:只显示源代码,不显示类信息
  • --lineNumber:显示行号信息
  • --code:显示字节码
  • -p:反编译所有方法和属性
  • -c:类加载器的hashcode

常用用法示例

1. 反编译整个类

# 反编译指定类 jad com.example.UserService

# 反编译内部类 jad com.example.OuterClass$InnerClass

2. 反编译特定方法

# 只反编译指定方法 jad com.example.UserService getUserById

# 反编译重载方法 jad com.example.UserService "getUserById(java.lang.String)"

3. 显示源代码(简洁模式)

# 只显示源代码,不显示额外信息 jad --source-only com.example.UserService

4. 显示行号信息

# 显示源代码和行号对应关系 jad --lineNumber com.example.UserService

5. 显示字节码

# 显示方法的字节码 jad --code com.example.UserService getUserById

6. 反编译所有内容

# 反编译类的所有方法和属性 jad -p com.example.UserService

7. 指定类加载器

# 当有多个类加载器时指定具体加载器 jad -c 3d4e5f com.example.UserService

实际应用场景

场景1:查看线上代码版本

# 确认线上运行的代码版本

jad --source-only com.example.OrderService processOrder

场景2:诊断代码问题

# 查看方法实现逻辑,排查问题 jad com.example.PaymentService --lineNumber

场景3:学习第三方库

# 查看框架或库的内部实现 jad org.springframework.beans.factory.BeanFactory

输出格式说明

jad命令的输出通常包含:

  • 类的基本信息(包名、类名)
  • 字段定义
  • 方法实现(源代码)
  • 行号映射信息

使用技巧

  1. 模糊匹配‌:可以使用通配符*进行模糊匹配
  2. 管道配合‌:可以配合grep等命令进行过滤
  3. 历史记录‌:使用history查看之前反编译的类
  4. 会话保持‌:在同一个Arthas会话中,可以直接使用类名

注意事项

  • jad反编译的是JVM中已加载的类,可能与源码有差异
  • 对于匿名内部类,类名会包含数字后缀
  • 某些混淆或优化的代码可能反编译结果不理想
  • 生产环境使用时要注意性能影响

jad命令是理解代码运行状态、诊断问题的强大工具,特别适用于无法直接查看源码的环境。

jad redefine mc

redefine命令基本语法

redefine /path/to/ClassFile.class

redefine -c <classloader-hash> /path/to/ClassFile.class

参数说明

  • /path/to/ClassFile.class:编译后的class文件路径
  • -c:指定类加载器的hashcode
  • --classLoaderClass:指定类加载器的类名

使用前提

  1. 只能修改方法体,不能添加/删除方法
  2. 不能修改字段定义
  3. 不能更改类继承关系
  4. 新类必须与旧类保持相同的包名和类名

完整使用流程

1. 准备修改的类

// 原始类:UserService.java
public class UserService {
    public String getUserInfo(Long userId) {
        return "User:" + userId;
    }
}

// 修改后类:UserService.java
public class UserService {
    public String getUserInfo(Long userId) {
        System.out.println("获取用户信息,用户ID:" + userId);
        return "Modified User:" + userId;
    }
}

2. 编译修改后的类

# 编译为class文件 javac UserService.java

# 确认编译成功 ls UserService.class

3. 执行redefine命令

# 在Arthas中执行redefine redefine /path/to/UserService.class

# 如果存在多个类加载器,需要指定 redefine -c 3d4e5f /path/to/UserService.class

实际应用场景

场景1:紧急修复线上bug

# 1. 定位问题类 jad --source-only com.example.BugService

# 2. 修改源码并编译

# 3. 热更新修复 redefine /tmp/BugService.class

# 4. 验证修复效果 watch com.example.BugService bugMethod

场景2:临时添加日志

# 在方法中添加调试日志 redefine /path/to/WithLogs.class

场景3:功能开关控制

# 动态启用/禁用某些功能 redefine /path/to/FeatureToggle.class

配合其他命令使用

1. 与jad配合

# 先查看当前代码

jad --source-only com.example.TargetClass

# 修改后重新定义 redefine /path/to/TargetClass.class

2. 与watch配合验证

# redefine后观察方法执行 watch com.example.TargetClass targetMethod params

3. 与sc配合查找类

# 查找类信息 sc -d com.example.TargetClass # 获取类加载器信息用于redefine

注意事项

限制条件

  • 不能增加方法‌:新类方法数量必须与旧类一致
  • 不能修改方法签名‌:包括参数、返回类型、方法名
  • 不能增删字段‌:字段结构必须保持一致
  • 不能改变继承关系‌:父类和接口不能改变

风险提示

  1. 内存泄漏风险‌:反复redefine可能导致PermGen/Metaspace溢出
  2. 兼容性问题‌:修改后的类必须与调用方兼容
  3. 事务一致性‌:修改数据库相关代码要注意事务状态
  4. 回滚准备‌:准备好回滚方案,防止修改引入新问题

最佳实践

  1. 测试验证‌:在测试环境充分验证后再上生产
  2. 小步修改‌:每次只修改少量代码,降低风险
  3. 监控观察‌:修改后密切监控应用状态
  4. 及时清理‌:完成修复后及时部署正式版本

错误处理

常见错误

# 类不匹配错误
redefine error! class: com.example.A not found in classloader

# 方法签名不匹配
redefine error! add method: xxx, please check your class file

# 解决方案
# - 确认类名完全一致
# - 检查类加载器是否正确
# - 验证修改是否超出限制范围

redefine命令是Arthas最强大的功能之一,正确使用可以快速解决线上问题,但需要谨慎操作并充分了解其限制和风险。

mc 命令详细用法

mc [options] [source-files] mc [options] [directory]

常用选项
  • -c, --classloader:指定类加载器的 hashcode
  • -d, --destination:指定编译输出的目录
  • --classLoaderClass:指定类加载器的类名
  • -S, --source:指定源码路径(JDK 9+ 支持模块化时使用)
  • -C, --classpath:指定额外的 classpath

完整使用示例

场景1:编译单个Java文件
# 1. 首先使用jad反编译线上类
jad --source-only com.example.UserService > /tmp/UserService.java

# 2. 编辑修改源码文件
vim /tmp/UserService.java

# 3. 使用mc编译修改后的源码
mc /tmp/UserService.java

# 4. 如果编译成功,会显示生成的.class文件路径
Memory compiler output:
/tmp/com/example/UserService.class
场景2:指定输出目录和类加载器
 
 

bashCopy Code

# 指定输出目录和类加载器 mc -d /tmp/classes -c 3d4e5f /tmp/UserService.java

# 编译多个文件 mc -d /tmp/classes /tmp/UserService.java /tmp/OrderService.java

场景3:编译整个目录

# 编译目录下的所有java文件 mc /tmp/src/

# 指定类路径编译 mc -C "/home/app/lib/*" /tmp/UserService.java

mc + redefine 完整工作流

步骤1:定位并导出问题类

# 查找要修改的类

sc *UserService

# 反编译得到源码

jad --source-only com.example.UserService > /tmp/UserService.java

步骤2:修改源码

编辑 /tmp/UserService.java,比如修改方法逻辑:

public class UserService {
    public String getUserInfo(Long userId) {
        // 添加日志和修改逻辑
        System.out.println("[DEBUG] 查询用户信息,ID: " + userId);
        return "Modified User Info: " + userId;
    }
}
步骤3:内存编译
# 编译修改后的源码
mc /tmp/UserService.java

# 输出示例:
Memory compiler output:
/tmp/com/example/UserService.class
Affect(row-cnt:1) cost in 320 ms
步骤4:热更新类
# 使用redefine加载编译后的类
redefine /tmp/com/example/UserService.class

# 输出示例:
redefine success, size: 1

步骤5:验证修改

# 观察方法执行情况 watch com.example.UserService getUserInfo params

输出信息解读

成功编译输出
Memory compiler output:
/tmp/com/example/UserService.class
Affect(row-cnt:1) cost in 450 ms
  • Memory compiler output‌:编译生成的.class文件路径
  • Affect(row-cnt:1)‌:成功编译的文件数量
  • cost in 450 ms‌:编译耗时
编译错误输出
Compilation error:
/tmp/UserService.java:15: error: cannot find symbol
    private NewType newField;
            ^
  symbol:   class NewType
  location: class UserService
Affect(row-cnt:0) cost in 280 ms
  • 会显示具体的编译错误信息
  • row-cnt:0‌ 表示没有文件编译成功

常见问题与解决方案

问题1:编译时找不到符号

原因‌:缺少依赖类
解决‌:

# 指定classpath mc -C "/path/to/dependency/*" /tmp/UserService.java

问题2:类加载器不匹配

原因‌:使用了错误的类加载器
解决‌:

# 先查看类的类加载器 sc -d com.example.UserService # 使用正确的类加载器hash mc -c 3d4e5f /tmp/UserService.java

问题3:包结构不一致

原因‌:源码的包路径与实际类不一致
解决‌:确保源码文件中的package声明与实际一致

注意事项

  1. 包路径必须一致‌:源码中的package声明必须与线上类完全一致
  2. 依赖问题‌:如果修改的类依赖其他类,需要确保这些类在classpath中可用
  3. 语法兼容性‌:使用的Java语法必须与目标JVM版本兼容
  4. 资源占用‌:频繁编译可能导致Metaspace内存增长

实际应用场景

紧急bug修复

# 快速修复线上空指针异常

jad --source-only com.example.BugService > /tmp/BugService.java

# 修改源码中的空指针问题

mc /tmp/BugService.java

redefine /tmp/com/example/BugService.class

添加调试信息
# 为怀疑有问题的方法添加日志
mc /tmp/WithDebugLogs.java
redefine /tmp/com/example/WithDebugLogs.class

mc命令大大简化了热更新的流程,避免了手动编译、上传等繁琐步骤,是线上问题紧急修复的重要工具。

更多命令后期更新。。。感谢阅读!!!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值