Arthas 实战:线上“看 Map”的四种武器

引子:一个真实的排查场景

凌晨两点,运维群里炸了锅:"配置中心的变更没生效!部分用户刷出来的数据不对!"

我登录跳板机,连上 Arthas,试图看一眼那个传说中的 domainHotConfigMap------一个存着所有域名配置的缓存 Map。按照惯例,我敲下:

bash 复制代码
watch com.example.HotConfigService getConfigMap 'returnObj' -x 3

然后等着请求触发。结果等了五分钟,这个方法压根没人调用------凌晨两点,哪来的流量?

这时候我才意识到:我需要一种不依赖请求就能看到 Map 内容的方法

这就是本文要讲的主题:在 Arthas 中,到底有几种方式可以查看 Map 的值?各自适用于什么场景?


为什么"看 Map"是线上排查的高频场景?

在 Java 后端系统里,Map 几乎是无处不在的数据结构:

场景 Map 的角色
本地缓存 存热点数据,减少 RPC 调用
配置中心客户端 存拉取下来的配置键值对
Session / 上下文 存请求维度的临时数据
聚合统计 存计数、求和等中间结果
静态常量池 存枚举映射、错误码映射

这些 Map 出问题时,症状往往是:数据不对、数据缺失、数据过多、数据不更新。而最尴尬的是,这些 Map 通常没有对外暴露的查询接口,日志里也不会把全部内容打出来。

Arthas 提供了 4 种"看 Map"的手段,分别对应不同的场景。下面逐一拆解。


武器一:getstatic --- 查看静态 Map

适用场景:Map 是 static 修饰的类属性,比如全局缓存、常量映射表。

命令格式

bash 复制代码
getstatic 类名 字段名 -x 展开深度

实战示例

bash 复制代码
getstatic com.example.CacheManager globalCache -x 3

按 Key 过滤(借助 OGNL):

bash 复制代码
getstatic com.example.CacheManager globalCache 'entrySet().iterator.{? #this.key=="userId_123"}' -x 3

优点 :无需请求触发,即时查看。

局限:只能看静态字段,普通实例字段无能为力。


武器二:watch --- 观测方法执行时的 Map

适用场景:想看在某个方法入参返回值中出现的 Map。

命令格式

bash 复制代码
watch 类名 方法名 '观察表达式' -x 展开深度

查看返回值 Map

bash 复制代码
watch com.example.OrderService getOrderMap 'returnObj' -x 3

同时查看入参和返回值

bash 复制代码
watch com.example.OrderService getOrderMap '{params[0], returnObj}' -x 3

带条件过滤(只关心特定参数):

bash 复制代码
watch com.example.OrderService getOrderMap 'returnObj' 'params[0]=="vip_user"' -x 3

优点 :能看到方法执行瞬间的 Map 快照,可以同时看入参和返回值做对比。

局限必须等待方法被调用才能触发输出,无流量时无法使用。


武器三:vmtool --- 无请求直接查堆内存

适用场景:最强大的武器。不管 Map 是静态还是实例属性,只要对象在堆里活着,就能直接捞出来看。

从 Arthas 3.5.1 开始支持,底层通过 JVMTI 接口直接查询堆中的对象实例。

查看某个 Bean 的实例属性 Map

bash 复制代码
vmtool --action getInstances --className com.example.HotConfigService --express 'instances[0].domainConfigMap' -x 3

只看 Map 大小(防止数据太多刷屏):

bash 复制代码
vmtool --action getInstances --className com.example.HotConfigService --express 'instances[0].domainConfigMap.size()' -x 1

按 Key 查找

bash 复制代码
vmtool --action getInstances --className com.example.HotConfigService --express 'instances[0].domainConfigMap.get("example.com")' -x 3

查看所有 Key

bash 复制代码
vmtool --action getInstances --className com.example.HotConfigService --express 'instances[0].domainConfigMap.keySet()' -x 2

优点 :不依赖请求,直接查堆,最通用。

注意instances[0] 假设是单例;如果类有多个实例,先用 instances.length 确认。


武器四:ognl --- 终极灵活但略复杂

适用场景:vmtoolgetstatic 覆盖不到的特殊情况,或者需要执行更复杂的表达式。

执行静态方法或读取静态字段

bash 复制代码
ognl '@com.example.CacheManager@getCacheMap()'

执行任意表达式

bash 复制代码
ognl 'new java.util.HashMap().put("key", "value")'

优点 :理论上什么都能做。

局限:需要熟悉 OGNL 语法,对新手不太友好。


三种主流武器对比

命令 适用场景 是否依赖请求 是否支持实例字段 上手难度
getstatic 静态 Map 属性 ❌ 不依赖 ❌ 不支持 ⭐ 简单
watch 方法入参/返回值的 Map ✅ 依赖 ✅ 支持 ⭐⭐ 中等
vmtool 任意对象的 Map 属性 ❌ 不依赖 ✅ 支持 ⭐⭐ 中等
ognl 复杂表达式 / 特殊场景 ❌ 不依赖 ✅ 支持 ⭐⭐⭐ 较难

实战心得

1. 最佳实践:vmtool 优先

如果你在排查一个缓存 Map 的内容不对 ,不知道这个 Map 什么时候会被更新,也不知道哪个方法会触发它------直接用 vmtool,一锤定音。

2. 带上 -x 参数

Arthas 默认的展开深度是 1,对于嵌套的 Map 结构,经常看到的是 {} 或引用地址。务必加上 -x 3 甚至 -x 5

3. 如果 Map 是 null

有时候取出来是 null,不一定是真的没数据,可能是懒加载。试试调用 getter 方法:

bash 复制代码
vmtool --action getInstances --className com.example.HotConfigService --express 'instances[0].getDomainConfigMap()' -x 3

4. 确认类加载器

如果出现 ClassNotFoundException,可能是类加载器不对。先用 sc -d 查类信息,拿到类加载器哈希,然后在 vmtool 后面加上 -c 类加载器哈希

5. 实战示例(来自真实场景)

某次排查中,我需要查看 LoadService 里的 configMap 内容:

bash 复制代码
vmtool -x 3 --action getInstances --className LoadService --express 'instances[0].configMap' -c 9f1388f9

输出结果:

复制代码
@ConcurrentHashMap[
    @String(
        key=@String['value'],
        oldValue=null,
        newValue=@String['value']
    )
]

oldValue=null, newValue="value" 可以看出这是一条新增的配置记录,而不是修改,帮助定位了配置加载的逻辑问题。


写在最后

Arthas 的命令很多,但对于"看 Map"这个需求,核心记住三句话:

有请求看方法 → 用 watch

静态缓存 → 用 getstatic

其他所有情况 → 用 vmtool

掌握了这三个命令,线上 Map 相关的数据问题基本可以做到"手到擒来",不用再靠猜、靠加日志、靠重新发布。

最后送大家一句排查口诀:

本地缓存不更新,vmtool 直接捞数据;

方法返回不对劲,watch 抓包看现场;

静态常量映射表,getstatic 查分明。

希望这篇文章能帮你在下次线上 Map 出问题时,少熬夜,多睡觉。


(本文基于 Arthas 3.7.x 版本,命令如有变动请参考官方文档。)

相关推荐
霸道流氓气质2 小时前
Spring AI异常处理与重试机制
java·人工智能·spring
郑州光合科技余经理3 小时前
本地生活服务系统:成品模块和定制接口怎么划界
java·前端·人工智能·后端·系统架构·php·ai编程
萧瑟余晖4 小时前
Java深入解析篇三十三之密封类与接口(Sealed Classes)详解
java·开发语言
论迹复利7 小时前
FreeRTOS 在 RISC-V 上是如何“点火“的 —— 从 main() 到第一个任务的完整链路
java·开发语言·risc-v
张宇Joaquin8 小时前
鲲鹏统一并行加速库KUPL--众核并行能力介绍
java·开发语言·网络
宸津-代码粉碎机8 小时前
AI攻防战升级!基于Spring AI构建Java应用自动免疫安全体系
java·大数据·开发语言·人工智能·python·安全·spring
2601_963749109 小时前
越华环保集团数字化污水治理:端边云采集架构与平台对接实现
java·大数据·架构
xcLeigh9 小时前
Go入门:整数类型的溢出与安全边界
java·安全·golang
源码宝10 小时前
SpringBoot+Vue2 诊所门诊管理系统,完整前后端源码,可直接部署上线
java·源码·his系统·门诊系统·程序代码·诊所系统·门诊his