Arthus常用命令

50 阅读2分钟

1. thread 命令示例:排查 CPU 占用过高

场景: 线上监控显示某个 Java 服务的 CPU 占用率突然飙升到 90% 以上,需要快速定位是哪个线程导致的。

命令及输出:

  1. 查找占用 CPU 最高的线程:

    $ thread -n 3
    
  2. 结果解读:

    PIDNameCPU %State
    52http-nio-8080-exec-198RUNNABLE
    49http-nio-8080-exec-21WAITING
    50Timer-00WAITING

    Arthas 自动将 CPU 占用率最高的线程信息放在了最前面。我们可以看到 http-nio-8080-exec-1 这个线程占用了 98% 的 CPU,并且处于 RUNNABLE 状态。

  3. 查看该线程的堆栈信息(关键步骤):

从上面的输出中,我们会看到线程 52 的完整堆栈,例如:

 "http-nio-8080-exec-1" Id=52 RUNNABLE
     at com.example.service.UserService.calculateSomething(UserService.java:120)
     - locked <0x1b2c4d8e> (a java.lang.String)
     at com.example.controller.UserController.getUser(UserController.java:45)
     ...
  • 结论: 堆栈信息显示,线程 52 正在 UserService.calculateSomething(UserService.java:120) 这个方法中执行,并且持续消耗 CPU。检查该方法代码后,发现其中有一个没有退出条件的死循环或计算复杂度极高的逻辑,从而定位问题。

2. trace 命令示例:定位请求慢的瓶颈点

场景: 用户反馈一个查询接口响应时间过长(例如 5 秒),想要知道这 5 秒都耗在了请求链路中的哪个环节。

命令:

我们要追踪 com.example.controller.OrderController 类中的 queryOrder 方法。

$ trace com.example.controller.OrderController queryOrder

结果解读:

输出会显示方法调用树和每一步的耗时:

`---ts=2025-11-19 22:45:00; [2000.0ms] com.example.controller.OrderController:queryOrder()`
    `---[10.0ms] com.example.service.OrderService:validate(int)`  // 参数校验很快
    `---[4900.0ms] com.example.service.OrderService:queryFromDatabase(int)` // **瓶颈点!**
    `---[80.0ms] com.example.util.DataConverter:format(java.lang.Object)` // 数据格式化

结论:

通过 trace 的精确计时,我们发现 queryFromDatabase 这个方法执行耗时 4900 毫秒,几乎占用了整个请求时间。这表明数据库查询是主要的性能瓶颈。下一步就可以针对这个方法进行优化(例如检查 SQL 语句、索引等)。

3. watch 命令示例:观测方法执行参数和返回值

场景: 线上一个注册接口偶尔会抛出空指针异常(NPE),但无法复现,想看看是什么样的输入参数导致了 NPE。

命令:

我们想要观测 com.example.service.UserService 类中的 register 方法,打印其入参异常信息

$ watch com.example.service.UserService register "{params, throwExp}" -e -x 2

参数解释:

  • "{params, throwExp}":OGNL 表达式,表示我们想要观测的字段(入参和抛出的异常)。
  • -e:表示只在方法抛出异常时才进行观测。
  • -x 2:表示输出结果的属性展开深度为 2,避免输出过多信息。

结果解读(假设异常发生):

当发生空指针异常时,Arthas 会打印类似以下信息:

ts=2025-11-19 22:48:30; [ERROR] 
... throwExp: java.lang.NullPointerException 
    at com.example.service.UserService.register(UserService.java:70) 
    ... 
params: 0: com.example.model.UserRegisterRequest@2b93f187 email=user@example.com    
    username=arthas_user password=*** 
// **关键发现:** 
// address=null address=<null>

结论:

从观测结果中发现,当 UserRegisterRequest 对象的 address 字段为 null 时,方法抛出了 NullPointerException。结合代码,发现 register 方法在处理 address 前没有进行空值检查。这使得问题得到了精确的定位,无需修改代码,就能知道是哪个输入值导致了问题。