线上CPU飙高,不重启怎么定位是哪行代码的问题
生产环境突然CPU 100%,不能随便重启,也没法加日志。以前只能干瞪眼,现在用Arthas,不停机就能定位问题。
Arthas是什么
阿里开源的Java诊断工具,可以在不重启应用的情况下:
- 查看哪个方法在消耗CPU
- 动态查看方法入参和返回值
- 查看方法调用链路和耗时
- 热更新代码(线上慎用)
- 查看类加载信息
官方地址:https://arthas.aliyun.com/
安装和启动
# 下载
curl -O https://arthas.aliyun.com/arthas-boot.jar
# 启动(会列出Java进程让你选)
java -jar arthas-boot.jar
# 或者直接指定PID
java -jar arthas-boot.jar <pid>
进入后会看到arthas的命令行界面。
实战场景一:CPU飙高定位
问题现象
服务器CPU持续100%,top看是Java进程。
排查步骤
Step 1:进入Arthas
java -jar arthas-boot.jar
Step 2:用thread命令找到CPU最高的线程
thread -n 3
输出:
"http-nio-8080-exec-15" Id=45 cpuUsage=98.5% RUNNABLE
at com.example.service.OrderService.calculatePrice(OrderService.java:156)
at com.example.controller.OrderController.createOrder(OrderController.java:78)
...
直接定位到OrderService.java的156行!
Step 3:查看具体代码
jad com.example.service.OrderService calculatePrice
反编译出源码,发现是个死循环或者复杂计算。
常用thread命令
# 查看最忙的3个线程
thread -n 3
# 查看所有线程状态
thread
# 查看死锁
thread -b
# 查看指定线程
thread 45
实战场景二:方法执行太慢
问题现象
某个接口响应时间突然变长,不知道慢在哪。
用trace命令追踪
trace com.example.service.OrderService createOrder
输出:
`---ts=2024-01-15 14:30:00;thread_name=http-nio-8080-exec-1;
`---[3256.123ms] com.example.service.OrderService:createOrder()
+---[1.234ms] com.example.service.OrderService:validateOrder()
+---[3200.456ms] com.example.service.OrderService:queryInventory() # 这里慢!
+---[50.123ms] com.example.service.OrderService:saveOrder()
一眼就看出queryInventory方法耗时3200ms,问题定位到了。
trace进阶用法
# 只显示耗时超过100ms的
trace com.example.service.OrderService createOrder '#cost > 100'
# 追踪多层调用(默认1层)
trace com.example.service.OrderService createOrder -n 5 --skipJDKMethod false
# 追踪多个方法
trace com.example.service.* *
实战场景三:查看方法入参和返回值
问题现象
方法逻辑有问题,想看看实际传了什么参数。
用watch命令
# 查看入参
watch com.example.service.UserService getUser params
# 查看返回值
watch com.example.service.UserService getUser returnObj
# 同时看入参和返回值
watch com.example.service.UserService getUser '{params, returnObj}'
输出:
ts=2024-01-15 14:35:00; result=@ArrayList[
@Object[][
@Long[12345],
],
@User[
id=@Long[12345],
name=@String["张三"],
status=@Integer[1],
],
]
watch进阶用法
# 只在抛异常时显示
watch com.example.service.UserService getUser '{params, throwExp}' -e
# 显示调用前和调用后
watch com.example.service.UserService getUser '{params, returnObj}' -b -s
# 条件过滤:只看userId=123的调用
watch com.example.service.UserService getUser '{params, returnObj}' 'params[0]==123'
# 展开对象属性(默认只显示1层)
watch com.example.service.UserService getUser returnObj -x 3
实战场景四:动态修改日志级别
线上日志级别是INFO,想临时看DEBUG日志。
# 查看当前日志级别
logger
# 修改日志级别
logger --name com.example.service --level DEBUG
# 恢复
logger --name com.example.service --level INFO
不用重启,不用改配置文件。
实战场景五:查看JVM信息
# 查看JVM参数
jvm
# 查看内存使用
memory
# 查看系统属性
sysprop
# 查看环境变量
sysenv
# 实时面板(类似top)
dashboard
dashboard会显示实时的线程、内存、GC情况,很直观。
实战场景六:反编译线上代码
怀疑线上代码和本地不一致:
# 反编译整个类
jad com.example.service.OrderService
# 只看某个方法
jad com.example.service.OrderService createOrder
# 不显示行号
jad --source-only com.example.service.OrderService
实战场景七:查看类加载信息
# 查看类是从哪个jar加载的
sc -d com.example.service.OrderService
# 查看类的所有方法
sm com.example.service.OrderService
# 模糊搜索类
sc *OrderService*
排查类冲突、jar包冲突很有用。
常用命令速查表
| 命令 | 作用 | 常用示例 |
|---|---|---|
| thread | 查看线程 | thread -n 3 |
| trace | 方法调用链路耗时 | trace ClassName methodName |
| watch | 查看方法入参/返回值 | watch ClassName methodName '{params,returnObj}' |
| stack | 查看方法调用栈 | stack ClassName methodName |
| tt | 时间隧道,记录调用 | tt -t ClassName methodName |
| jad | 反编译 | jad ClassName |
| sc | 查看类信息 | sc -d ClassName |
| sm | 查看方法信息 | sm ClassName |
| logger | 查看/修改日志级别 | logger --name xxx --level DEBUG |
| dashboard | 实时面板 | dashboard |
| memory | 内存信息 | memory |
| heapdump | 导出堆快照 | heapdump /tmp/dump.hprof |
tt命令:时间隧道
记录方法调用,可以事后回放:
# 开始记录
tt -t com.example.service.OrderService createOrder
# 查看记录列表
tt -l
# 查看某次调用的详情
tt -i 1000
# 重新执行某次调用(危险!)
tt -i 1000 -p
ognl表达式
Arthas支持ognl表达式,可以做很多事:
# 调用静态方法
ognl '@java.lang.System@getProperty("java.version")'
# 获取Spring Bean并调用方法
ognl '@com.example.SpringContextHolder@getBean("userService").getUser(123)'
# 查看静态变量
ognl '@com.example.config.AppConfig@MAX_RETRY_COUNT'
远程诊断
如果服务器在内网没有公网IP,可以用星空组网把本地和服务器连起来。组网后直接SSH上去运行Arthas,或者用Arthas的tunnel server做Web端诊断,比开VPN方便。
退出Arthas
# 退出当前会话,不影响目标进程
quit
# 完全退出,从目标进程卸载Arthas
stop
注意事项
- 生产环境慎用:trace、watch等命令有性能开销
- 用完记得stop:否则Arthas会一直attach在进程上
- 权限控制:Arthas能做的事很多,注意权限管理
- 版本匹配:Arthas版本和JDK版本要兼容
总结
| 场景 | 命令 |
|---|---|
| CPU飙高 | thread -n 3 |
| 方法慢 | trace |
| 看入参返回值 | watch |
| 看调用栈 | stack |
| 改日志级别 | logger |
| 反编译代码 | jad |
| 类加载问题 | sc -d |
Arthas是Java程序员的瑞士军刀,建议每个Java开发都要熟练掌握。

浙公网安备 33010602011771号