Java Arthas 线上排障 2026-07-13
线上问题排查是每个后端开发者的日常难题。当接口突然变慢、配置莫名失效、变量值不符合预期时,我们往往需要在不重启服务的前提下,快速定位问题根因。Arthas(Alibaba Java Diagnostic Tool)正是为此而生——它让我们在线上环境"透视" JVM 内部状态,堪称 Java 排障的"瑞士军刀"。
在千阶智能的技术团队中,Arthas 已经是线上排障的标准工具。本文结合千阶智能平台(qjchat.com)真实生产案例,系统讲解 Arthas 五大核心用法,助你从"会敲命令"进阶到"能排障"。
一、观察方法耗时——trace / stack
1.1 命令说明
| 命令 | 用途 | 前提 |
|---|---|---|
trace | 追踪方法调用链及每一层耗时 | 有流量经过实例 |
stack | 输出方法被调用的调用路径 | 有流量经过实例 |
1.2 实战案例:接口慢在哪里?
千阶智能外呼系统接口出现响应超时,第一步就是用 trace 逐层定位耗时瓶颈:
# 追踪 WordQueryAPIImpl.getWord 的调用链耗时
trace com.qianjie.smart.api.word.WordQueryAPIImpl getWord
输出结果会以树状结构展示每一层调用的耗时,一目了然地看到哪个子方法是"罪魁祸首":
`---[123.45ms] com.qianjie.smart.api.word.WordQueryAPIImpl:getWord()
+---[2.31ms] com.qianjie.smart.api.word.WordQueryAPIImpl:validateParam() #45
+---[5.67ms] com.qianjie.smart.api.word.WordQueryAPIImpl:preProcess() #52
+---[110.23ms] com.qianjie.smart.api.word.WordQueryAPIImpl:doResponse() #68
`---[1.12ms] com.qianjie.smart.api.word.WordQueryAPIImpl:postProcess() #75
从输出可以看到 doResponse 耗时 110ms,是整个接口的瓶颈。假设发现 doResponse 中主要是 transform2RespWithFilter 在耗时,则继续深入:
# 继续追踪下一层
trace com.qianjie.smart.factory.ModelFactoryImpl transform2RespWithFilter
`---[105.89ms] com.qianjie.smart.factory.ModelFactoryImpl:transform2RespWithFilter()
+---[0.45ms] com.qianjie.smart.factory.ModelFactoryImpl:getFilterList() #120
+---[98.76ms] com.qianjie.smart.filter.WordFilterChain:doFilter() #135
| +---[50.12ms] com.qianjie.smart.filter.WordMatchFilter:doFilter() #45
| `---[45.67ms] com.qianjie.smart.filter.WordRankFilter:doFilter() #78
`---[3.21ms] com.qianjie.smart.factory.ModelFactoryImpl:buildResponse() #150
可以看到 WordFilterChain:doFilter 耗时 98ms,其中 WordMatchFilter 和 WordRankFilter 分别消耗了 50ms 和 45ms,这就是根因所在。
排障思路:像剥洋葱一样,从入口方法开始,逐层
trace,直到定位到真正的耗时节点。
1.3 进阶用法
# 过滤耗时大于 100ms 的调用
trace com.example.Service method '#cost > 100'
# 排除 jdk 内部调用路径
trace com.example.Service method --exclude-class-pattern java.*
# 追踪多次调用(默认只追踪一次)
trace com.example.Service method -n 5
二、观察方法入参、返回值——watch
2.1 命令说明
watch 是 Arthas 中最灵活的命令之一,可以观察方法的入参(params)、返回值(returnObj)、异常(throwExp)以及目标对象(target)。
| 观察点 | 参数 | 说明 |
|---|---|---|
| 方法调用前 | -b | 观察入参 |
| 方法返回后 | 默认 | 观察返回值 |
| 方法异常后 | -e | 观察异常 |
| 同时观察 | -b -s -e | 全生命周期 |
2.2 实战案例:连接池配置是否生效?
问题背景:千阶智能平台配置了连接池最大值为 200,但疑似未生效。
配置文件如下:
spring:
datasource:
hikari:
maximum-pool-size: 200
通过 Arthas 验证实际生效值:
# 只查看最大连接数
watch com.zaxxer.hikari.HikariConfig getMaximumPoolSize
Affect(class count: 1, method count: 1) cost in 45 ms, listenerId: 1
method=com.zaxxer.hikari.HikariConfig.getMaximumPoolSize location=AtExit
ts=2026-07-13 10:23:45; result=@Integer[10]
# 查看所有属性(展开2层)
watch com.zaxxer.hikari.HikariConfig getMaximumPoolSize -x 2
Affect(class count: 1, method count: 1) cost in 38 ms, listenerId: 2
method=com.zaxxer.hikari.HikariConfig.getMaximumPoolSize location=AtExit
ts=2026-07-13 10:24:12; result=@Integer[10]
@HikariConfig[
maximumPoolSize=@Integer[10],
minimumIdle=@Integer[10],
idleTimeout=@Long[600000],
maxLifetime=@Long[1800000],
connectionTimeout=@Long[30000],
...
]
结果分析:
- 配置最大连接数为 200,实际生效为 10 → 不符合预期,配置未生效
- 配置最大连接数为 5,实际生效为 5 → 符合预期
关键洞察:
watch让我们在不重启、不加日志的情况下,直接"看到"运行时的变量值,快速验证配置是否按预期加载。
2.3 进阶用法
# 观察入参和返回值
watch com.example.Service method '{params, returnObj}' -x 3
# 按条件过滤(只观察第一个参数大于 100 的调用)
watch com.example.Service method '{params, returnObj}' 'params[0] > 100'
# 观察异常
watch com.example.Service method '{params, throwExp}' -e
# 观察调用耗时
watch com.example.Service method '#cost'
三、获取与修改 JVM 变量——ognl
这是 Arthas 最强大的能力之一:在运行时读取甚至修改 JVM 内部的任意变量,无需重启服务。
3.1 静态变量
# 获取静态变量
ognl '@com.example.Config@DEFAULT_VALUE'
# 修改静态变量
ognl '@com.example.Config@DEFAULT_VALUE="new_value"'
3.2 实例变量
| 场景 | 获取方式 | 修改方式 |
|---|---|---|
| 有流量通过 | watch | watch + ognl 表达式 |
| 无流量通过 | springbean + ognl | springbean + ognl |
3.3 实战案例:无流量时修改实例变量
千阶智能平台在凌晨无流量时段,需要紧急修改某个业务服务的实例变量。
第一步:准备 BeanFactoryHelper
在项目中新增一个静态 Bean 工厂类,通过 Spring 的 BeanFactoryAware 回调获取 BeanFactory:
@Component
public class BeanFactoryHelper implements BeanFactoryAware {
private static BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
BeanFactoryHelper.beanFactory = beanFactory;
}
public static Object getBean(String beanName) {
return beanFactory.getBean(beanName);
}
public static <T> T getBean(Class<T> requiredType) {
return beanFactory.getBean(requiredType);
}
public static boolean containsBean(String beanName) {
return beanFactory.containsBean(beanName);
}
}
第二步:获取目标类的 ClassLoader Hash
sc -df com.qianjie.smart.infrastructure.bs.BsQueryService
输出中重点关注 classLoaderHash,后续 ognl 命令需要用到:
class-info com.qianjie.smart.infrastructure.bs.BsQueryService
code-source file:/home/work/java/smart-app/target/smart-app.jar!/BOOT-INF/lib/smart-infrastructure-1.0.0.jar!/
name com.qianjie.smart.infrastructure.bs.BsQueryService
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name BsQueryService
modifier public
annotation org.springframework.stereotype.Component
interfaces
super-class +-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@6767c1fc
+-jdk.internal.loader.ClassLoaders$AppClassLoader@2f333739
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@1172f803
classLoaderHash 6767c1fc
fields name log
type org.slf4j.Logger
modifier final,private,static
value Logger[com.qianjie.smart.infrastructure.bs.BsQueryService]
name protocolSeparator
type java.lang.String
modifier
name enter
type java.lang.String
modifier
name defaultKey
type java.lang.String
modifier
第三步:查看变量
ognl -c 6767c1fc '#instance=@com.qianjie.smart.utils.BeanFactoryHelper@getBean("bsQueryService")'
输出:
@BsQueryService[
log=@Logger[Logger[com.qianjie.smart.infrastructure.bs.BsQueryService]],
protocolSeparator=@String[\r\n.\r\n],
enter=@String[\r\n],
defaultKey=@String[99],
]
第四步:修改变量
ognl -c 6767c1fc '#instance=@com.qianjie.smart.utils.BeanFactoryHelper@getBean("bsQueryService"),
#fieldObj=@com.qianjie.smart.infrastructure.bs.BsQueryService@class.getDeclaredField("defaultKey"),
#fieldObj.setAccessible(true),
#fieldObj.set(#instance,"88")'
修改后再次观察,确认 defaultKey 已从 "99" 变为 "88":
@BsQueryService[
...
defaultKey=@String[88],
]
核心原理:通过 Spring BeanFactory 获取实例引用,再通过反射
setAccessible(true)突破访问控制,直接修改私有字段值。整个过程无需重启 JVM。
四、类加载与热替换 Class
当线上代码有 Bug 但无法立即发版时,Arthas 支持热替换 Class 文件:
# 查看类加载信息
sc -df com.example.TargetClass
# 反编译线上代码(确认当前逻辑)
jad com.example.TargetClass methodName
# 热替换(用本地编译好的 class 文件替换线上的)
redefine /path/to/TargetClass.class
注意事项:
redefine后原来的类不能被 GC,需注意内存影响- 优先使用
retransform,它支持回滚操作 - 热替换是临时方案,务必在下一版本中正式修复
五、CPU 与内存热点分析
5.1 CPU 线程热点
# 查看最消耗 CPU 的线程
thread -n 5
# 查看指定线程的堆栈
thread <thread-id>
# 查找死锁
thread -b
5.2 CPU 方法热点
# 持续采样,输出最耗 CPU 的方法
profiler start --event cpu
# ... 运行一段时间 ...
profiler stop
5.3 内存方法热点
# 分析内存分配热点
profiler start --event alloc
# ... 运行一段时间 ...
profiler stop
建议:profiler 采样时间建议 30s-60s,太短数据不准,太长影响性能。
六、经典排障案例总结
Case 1:接口太慢,定位耗时根因
关键:像剥洋葱一样逐层追踪,不要一次 trace 太深。
Case 2:变量值是否符合预期
| 场景 | 命令 |
|---|---|
| 有流量经过 | watch 观察入参/返回值 |
| 无流量经过 | springbean + ognl 直接读取 |
Case 3:配置中心丢失,需紧急修改配置
场景:千阶智能配置中心挂了,不能重启(一重启就挂),但需要修改某个 VIP 地址或域名。
方案一:Arthas 热修改
# 通过 springbean + ognl 直接修改 JVM 内部的配置变量
ognl -c <classLoaderHash> '#instance=@com.example.BeanFactoryHelper@getBean("configBean"),
#field=@com.example.ConfigBean@class.getDeclaredField("targetVip"),
#field.setAccessible(true),
#field.set(#instance,"10.10.10.10")'
方案二:内核四层转发(需 root 权限)
# 配置 iptables 将旧 VIP 的流量转发到新 VIP
iptables -t nat -A PREROUTING -d 10.11.12.13 -j DNAT --to-destination 10.10.10.10
方案选择:优先用 Arthas 热修改(影响范围小),只有在 Arthas 无法触达的场景才用 iptables。
七、Arthas 命令速查表
| 场景 | 命令 | 说明 |
|---|---|---|
| 方法耗时 | trace 类名 方法名 | 逐层追踪调用链耗时 |
| 调用路径 | stack 类名 方法名 | 查看方法被谁调用 |
| 入参/返回值 | watch 类名 方法名 '{params,returnObj}' | 观察方法入参和返回值 |
| 静态变量 | ognl '@类名@变量名' | 读取/修改静态变量 |
| 实例变量 | ognl + springbean | 读取/修改实例变量 |
| 类信息 | sc -df 类名 | 查看类加载信息 |
| 反编译 | jad 类名 方法名 | 查看线上代码 |
| 热替换 | redefine /path/to/Class.class | 热替换 Class 文件 |
| CPU 热点线程 | thread -n 5 | Top 5 CPU 消耗线程 |
| 死锁检测 | thread -b | 查找死锁 |
| 性能采样 | profiler start/stop | CPU/内存热点分析 |
结语
Arthas 的核心价值在于不重启、不侵入、实时诊断。掌握 trace、watch、ognl 三板斧,足以应对 80% 的线上排障场景。而 redefine 和 profiler 则是进阶利器,在紧急修复和性能调优中发挥关键作用。
更多千阶智能技术实践文章,请访问 qjchat.com。
排障黄金法则:
- 先
trace定位耗时,再watch验证数据 - 有流量用
watch,无流量用ognl - 热修改是临时方案,务必正式修复
profiler采样控制时长,避免影响线上性能
本文由千阶智能技术团队出品,转载请注明出处。更多技术内容请访问 qjchat.com。