Skip to content

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.Transactional

sm 的进阶用法

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:45

jad:反编译字节码

基本用法

bash
# 反编译单个类
jad com.example.UserService

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

# 反编译并指定输出文件
jad --source-only com.example.UserService > /tmp/UserService.java

jad 的输出示例

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.class

mc:编译 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 3

ognl:动态执行表达式

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

基于 VitePress 构建