sc/sm/jad/monitor/watch 命令
Arthas 高级命令:深入运行时
这一节介绍 Arthas 中更高级的命令——jad、mc、redefine 用于代码热修复;ognl 用于动态执行表达式;finally 用于增强方法。
sc 和 sm:深入类和方法
sc 的进阶用法
bash
# 搜索包含特定字符串的所有类
sc *UserService*
# 查看类的完整信息(字段、方法、父类、接口、注解)
sc -d com.example.UserService
# 输出:
# class-info com.example.UserService
# class-loader sun.misc.Launcher$AppClassLoader@7c2d1a3c
# super-class java.lang.Object
# modifiers public
# interfaces com.example.BaseService, java.io.Serializable
# fields
# private com.example.UserRepository userRepository
# private int timeout
# methods
# public java.util.List<com.example.User> findAll()
# public com.example.User findById(java.lang.Long)
# private void validateUser(com.example.User)
# public static com.example.User create(java.lang.String, int)
# annotations
# @org.springframework.stereotype.Service
# @org.springframework.transaction.annotation.Transactionalsm 的进阶用法
bash
# 搜索特定方法
sm com.example.UserService *Name*
# 查看方法的行号信息
sm -d com.example.UserService findById
# 输出:
# declaring-class com.example.UserService
# method-name findById
# modifier public
# annotation @org.springframework.transaction.annotation.Transactional(readOnly=true)
# parameters (java.lang.Long)
# return-type com.example.User
# line 45
# locations 45
# step 1 userService.java:45jad:反编译字节码
基本用法
bash
# 反编译单个类
jad com.example.UserService
# 反编译指定方法
jad com.example.UserService findById
# 反编译并指定输出文件
jad --source-only com.example.UserService > /tmp/UserService.javajad 的输出示例
ClassLoader:
sun.misc.Launcher$AppClassLoader@7c2d1a3c
Location:
file:/home/app/lib/myapp.jar
/*
* Decompiled with CFR.
*/
package com.example;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findById(Long id) {
return this.userRepository.findById(id).orElse(null);
}
}实际应用场景
bash
# 场景:线上代码和预期不一致
# 1. 查看当前运行的代码
jad com.example.UserService > /tmp/current.java
# 2. 对比源码
diff /tmp/current.java /home/app/src/UserService.java
# 3. 确认问题后进行热修复
mc /tmp/fixed.java -d /tmp
redefine /tmp/com/example/UserService.classmc:编译 Java 文件
bash
# 编译单个文件(Arthas 会自动查找 ClassLoader)
mc /tmp/UserService.java
# 指定输出目录
mc /tmp/UserService.java -d /tmp/classes
# 编译整个目录
mc -c 7c2d1a3c /tmp/src -d /tmp/classes
# -c 指定 ClassLoader(用 sc -d 查看到的 hash)redefine:热替换
redefine 是 Arthas 最强大的功能之一——它可以在不重启 JVM 的情况下替换类。
基本用法
bash
# 替换类
redefine /tmp/com/example/UserService.class
# 替换多个类
redefine /tmp/class1.class /tmp/class2.class热替换的注意事项
redefine 的限制:
1. 不能添加/删除字段和方法
2. 不能修改方法签名
3. 已有方法只能修改方法体
4. 不能修改类结构
5. 如果类已被加载新版本的方法调用,需要用 redefine 重新加载
6. 不能修改构造函数
推荐方案(热修复的完整流程):
1. jad 反编译当前代码
2. 手动修改源码
3. mc 编译新代码
4. redefine 替换类热修复的完整示例
bash
# 1. 反编译当前代码
jad --source-only com.example.UserService > /tmp/UserService.java
# 2. 修改代码(在文件中修复 bug)
# vim /tmp/UserService.java
# ... 修复代码 ...
# 3. 编译修复后的代码
mc /tmp/UserService.java -d /tmp
# 4. 热替换
redefine /tmp/com/example/UserService.class
# 5. 验证修复
watch com.example.UserService findById '{params, returnObj}' -n 3ognl:动态执行表达式
ognl 是 Arthas 中最灵活的表达式工具,可以在运行时执行任意表达式。
基本用法
bash
# 调用静态方法
ognl '@com.example.MathUtil@max(10, 20)'
# 获取静态字段
ognl '@com.example.Config@ENABLE_CACHE'
# 调用实例方法
ognl '#user = @com.example.UserService@getCurrentUser(); #user.name'
# 获取 Spring Bean
ognl '@org.springframework.beans.factory.BeanFactoryUtils@beansOfTypeIncludingAncestors(@org.springframework.web.context.support.WebApplicationContextUtils@getRequiredWebApplicationContext(@javax.servlet.ServletContext@currentServletContext), com.example.UserService).values().iterator().next()'ognl 的实际应用
bash
# 场景:动态修改配置(无需重启)
# 查看当前配置
ognl @com.example.Config@MAX_RETRY_COUNT
# 输出:3
# 修改配置
ognl '@com.example.Config@setMAX_RETRY_COUNT(5)'
# 场景:动态清理缓存
ognl '@com.example.Cache@clear()'
# 场景:获取当前登录用户
ognl @com.example.SecurityContext@currentUser
# 场景:调用 Spring Bean
ognl @userService.findById(1L)ognl 常用语法
bash
# 调用静态方法
@ClassName@method()
# 获取/设置字段
#field.get(obj)
#field.set(obj, value)
# 集合操作
#list.size()
#list[0]
#map.get(key)
# 三元表达式
#condition ? value1 : value2
# null 检查
#obj?.method() // obj 为 null 时不抛异常monitor:方法调用统计
进阶参数
bash
# 按条件过滤
monitor -c 10 --regex com.example.*.get* -E '(find|get)'
# 只看异常
monitor -e com.example.UserService *
# 指定方法过滤
monitor -m findById com.example.UserService
# 输出示例:
# timestamp class method total success fail avg-rt(ms) fail-rate
# 2026-03-22 10:00 UserService findById 120 115 5 12.34 4.17%
# 2026-03-22 10:00 UserService findAll 80 80 0 3.21 0.00%watch:深入观察方法
watch 进阶表达式
bash
# 观察入参、返回值、异常、对象深度
watch com.example.UserService findById '{params, returnObj, throwExp}' -x 3
# 观察 before-return-after 三个阶段
# before → 方法执行前
# return → 方法返回后(正常返回)
# throw → 方法抛出异常后
watch com.example.UserService findById '{params, returnObj}' -b -f
# 观察入参和返回值(不观察异常)
watch com.example.UserService findById '{params, returnObj}' -e
# 条件过滤:只看参数等于特定值的情况
watch com.example.UserService findById '{params, returnObj}' 'params[0] == 1L'watch 的对象展开深度
bash
# -x 指定展开深度(默认 1)
watch com.example.UserService findById '{params[0]}' -x 1
# 输出:User(id=1, name=Alice, ...)
watch com.example.UserService findById '{params[0]}' -x 2
# 输出:User(id=1, name=Alice, email=alice@example.com, orders=[Order(...), ...])
watch com.example.UserService findById '{params[0]}' -x 3
# 输出:完全展开watch 的使用场景
bash
# 场景一:排查接口返回异常
watch com.example.UserService getUser '{params, returnObj, throwExp}' -e
# 场景二:观察参数变化
watch com.example.OrderService createOrder '{params[0].items.size()}' -b
# 场景三:分析性能(结合 trace)
trace com.example.OrderService createOrder '#cost > 100'
watch com.example.OrderService createOrder '{params}' '#cost > 100'trace + stack:追踪调用链路
trace 的进阶用法
bash
# 追踪方法并显示调用次数
trace com.example.UserService getUser '#cost > 0' -n 10
# 忽略特定类(减少噪音)
trace -j com.example.Logger com.example.UserService getUser
# -j → jump 跳过指定的类
# 输出示例:
# `---[15ms] com.example.UserService:getUser()
# +---[3ms] com.example.UserDAO:findById()
# | +---[1ms] java.sql.Connection:prepareStatement()
# | `---[1ms] java.sql.Statement:executeQuery()
# +---[2ms] com.example.Cache:get()
# | `---[1ms] java.util.HashMap:get()
# `---[10ms] com.example.Processor:process()stack 的进阶用法
bash
# 查看方法被调用的完整堆栈
stack com.example.Cache get -n 5
# 按条件过滤
stack com.example.Cache get '#params[0] == "user:123"'增强命令组合
组合一:找到慢方法并分析
bash
# 1. 先用 trace 找到慢方法
trace com.example.* * '#cost > 100' -n 5
# 2. 确认慢方法后,用 watch 观察入参
watch com.example.SlowService slowMethod '{params, returnObj}' -b -f -x 2
# 3. 查看调用堆栈
stack com.example.SlowService slowMethod -n 10组合二:分析内存泄漏嫌疑人
bash
# 1. 生成堆转储
heapdump --live /tmp/heap.hprof
# 2. 分析哪些类实例最多
jvm | grep "heap"
# 3. 观察哪些方法创建了这些对象
watch com.example.Cache put '{params, returnObj}' -b
# 4. 追踪方法调用
trace com.example.Cache put '#cost > 10'本节小结
Arthas 高级命令速查:
| 指令 | 作用 | 进阶参数 |
|---|---|---|
| jad | 反编译字节码为源码 | --source-only 输出源码 |
| mc | 编译 Java 文件 | -d 输出目录,-c ClassLoader |
| redefine | 热替换类 | 只能替换方法体 |
| ognl | 动态执行表达式 | 调用静态/实例方法、获取字段 |
| monitor | 方法调用统计 | -c 周期,-e 只看异常 |
| watch | 观察方法执行 | -b/-f/-e 三阶段,-x 展开深度 |
| trace | 追踪调用路径 | -j 跳过类,#cost > N 过滤 |
| stack | 查看调用堆栈 | 条件过滤 |
热修复流程:jad → mc → redefine → 验证。ognl 是最灵活的工具,可以动态执行任意代码。
下一节,我们来看 Arthas 实战案例。
