精准抓某次调用需指定-s或-b、加condition-express过滤、用-x展开对象;returnobj为null常见于异步或aop代理场景;params[0]异常多因重载/泛型擦除;线上慎用-x>2、-n过大或无条件-b。

watch怎么精准抓到某次调用的入参和返回值
直接用 watch 默认会刷屏,尤其在高并发服务里,一次请求可能触发几十次目标方法调用,你真正关心的那一次根本找不到。关键不是“能不能看”,而是“怎么只看它”。
- 必须加
-s(返回后)或-b(调用前),否则默认-f(结束时)会把异常/正常都混在一起输出,干扰判断 - 一定要写
condition-express过滤条件,比如params[0].id == 12345或#cost > 500,不然等于开盲盒 - 观察表达式别偷懒写
{params, returnObj}就完事——如果参数是复杂对象,不加-x 3只能看到@User[...]这种占位符,实际字段全被折叠
为什么有时候returnObj是null,但方法明明返回了值
常见于异步方法、CompletableFuture链、或者被AOP代理拦截后未正确传递返回值的场景。watch看到的 returnObj 是JVM字节码层面真实返回的对象,不是Spring @Async封装后的结果。
- 如果方法声明返回
CompletableFuture<string></string>,returnObj就是那个CompletableFuture实例,不是它内部的String;想看实际结果得写returnObj.get()(但注意阻塞风险) - Spring事务代理下,原始方法返回非null,但被
TransactionInterceptor包裹后,watch可能在代理层触发,看到的是代理逻辑的中间态 -
-s和-f对返回值捕获行为不同:-s只在return指令执行后取值,-f在 finally 块之后,若方法里有 try-catch-return-finally,二者可能拿到不同对象
params[0]取不到值或报OGNL异常怎么办
不是代码写错了,大概率是方法重载或泛型擦除导致参数类型识别偏差,Arthas按字节码签名匹配,不走Java源码语义。
- 确认类名方法名是否带通配符误匹配:比如
com.example.*Service可能同时命中UserServiceImpl和OrderServiceImpl,而后者参数结构不同 - 泛型方法如
<t> T parse(String json)</t>,字节码里params[0]是String没问题,但若写params[0].length()就安全;写params[0].getId()就会抛java.lang.NoSuchMethodException - 静态方法没有
target,但params依然可用;实例方法若在构造器里调用,target可能为 null,但params不受影响
线上环境慎用的三个参数组合
watch本质是字节码增强,高频触发会对性能造成肉眼可见影响,尤其在CPU已紧张的节点上。
- 别用
-x 5或更高——深度展开会递归调用toString(),某些对象(如Hibernate Proxy、Logback Logger)可能触发无限循环或死锁 - 避免无条件 +
-n 100:指定次数看似可控,但如果每秒调用200次,100次限制几秒就满了,后续调用完全漏监控 - 禁止单独用
-b监控 setter 类方法:比如setUserName(String),params[0]是新值,但target此时可能还未完成字段赋值,读target.userName会得到旧值甚至 null
最稳的线上姿势是:明确类+方法+条件表达式+-s+-x 2,等看到有效数据再逐步放开范围。复杂对象字段访问优先用 params[0].getXXX() 而非直接点属性,避开 getter 不存在或 NPE 的坑。










