Arthas vmtool 实战:不靠请求也能摸到 JVM 里的对象

简介: watch/trace 等不到请求?用 Arthas vmtool 直接翻堆里实例、字段和 Spring Bean,低 QPS 也能当场看清状态

大家好,我是程序员天天困。

这是「Arthas 线上诊断实战」系列第 3 篇,专门聊 Arthas vmtool。前两篇 watchtrace 都很强,但有一种场景也很常见:字段看着不对,接口却半天没人点,watch/trace 干挂着使不上劲。

vmtool 不靠请求触发,直接从堆里把对象实例捞出来——读字段、调方法、验 Spring Bean。接口半天没人调、定时任务不知道啥时候跑、根本不知道怎么触发的时候,它最好用。点个收藏,我们直接上手。

一、watch / trace 的死角:没请求就盯不住

watch 和 trace 都是「请求触发式」的诊断。 什么意思?

就是得有人调用那个方法,它们才能看到东西。没人调用,监听器就一直空转,啥也看不到。

举个最常见的例子:线上某个本地缓存 Map 越攒越大,你怀疑该清理的 entry 没被清掉,可这个 Map 既没对外暴露接口、也没打日志。watch 盯不住(没人调它的方法),trace 也无从下手。这时候用 vmtool 直接把那个缓存对象从堆里捞出来,看一眼 size() 就知道攒了多少——这就是 vmtool 最值钱的场景。

你可以这么理解:watch / trace 像路口的摄像头,车来了才拍;vmtool 像仓库的钥匙,门不开也能进去翻货架上的货。

二、Arthas vmtool 是什么:给堆里的对象开个口子

Arthas vmtool(JVM 工具命令):Arthas 从 3.5.1 开始带的命令,底层走 JVMTI 接口,能查堆里的对象实例、执行表达式、强制 GC 这些。说白了就是「不用等请求,直接去 JVM 里把对象翻出来看」。

JVMTI(JVM Tool Interface):JDK 提供的原生调试和监控接口,Arthas vmtool 就是靠它去堆里找实例的。

日常排查里用得最多的是 --action getInstances:按类名把堆里还活着的实例找出来,结果放到 instances 数组里,再用 --express 写 OGNL 表达式去读字段、调方法。

可能有人会问:vmtool 和 sc / jad 有啥区别?

sc / jad 看的是本身(类信息、反编译出来的源码);getInstances 看的是对象实例。类只有一份定义,实例可以有一堆——你要的是「现在这个 Bean 里字段是多少」,那必须查实例。

IDEA 装了 Arthas 插件的话,在字段上右键,菜单里一般能直接生成 vmtool 命令,省得手敲全限定名。

三、Arthas vmtool getInstances:把私有字段翻出来看

系列还是用 com.ttk 包。假设有个订单统计服务,计数器只有累加和重置,没对外暴露 getter

@Service
public class OrderStatsService {
   
    private final AtomicLong orderTotal = new AtomicLong(0);
    private volatile String lastRegion = "unknown";

    public void record(String region) {
   
        orderTotal.incrementAndGet();
        lastRegion = region;
    }

    public void reset() {
   
        orderTotal.set(0);
        lastRegion = "unknown";
    }
}

线上你怀疑 orderTotal 已经飘了,又不想为了打个日志再发一版。Attach 好进程(第 1 篇讲过),直接来一条:

vmtool -x 3 --action getInstances \
  --className com.ttk.service.OrderStatsService \
  --express 'instances[0].orderTotal' --limit 5

拆开看:

  1. --action getInstances:按类找实例,不是找类定义。
  2. --className:全限定类名。
  3. --express:对结果做表达式;instances 是官方约定的实例数组。
  4. -x 3:展开层级,和 watch 一样,字段嵌套深就往大调。
  5. --limit 5:限制返回数量。官方默认是 10,堆里同类实例多的时候记得调小点,别把 JVM 压出问题。

Spring 单例一般只有一个实例,所以 instances[0] 就够了。要是原型 Bean 或者你自己 new 出来的对象,先别加 --express,看下数组有多长,再决定用第几个。

JDK 9+ 的模块系统下,直接打印 AtomicLong 对象时,Arthas 要展开它内部的 value 字段,但受模块导出限制可能报错。更稳的写法是调 .get() 拿到具体值,走的是公开方法,不受影响:

vmtool -x 3 --action getInstances \
  --className com.ttk.service.OrderStatsService \
  --express 'instances[0].orderTotal.get()' --limit 5

能把没 getter 的私有字段当场读出来,这才是 vmtool 日常最值钱的地方。 平时为了看一个私有字段,要么改代码加 getter 重新发版,要么翻日志猜——vmtool 一条命令直接搞定。

四、对着实例直接调方法:看完了还能动手

既然 instances[0] 已经是那个活对象了,表达式里当然也能调它的方法。比如先手动累一次,再读:

vmtool --action getInstances \
  --className com.ttk.service.OrderStatsService \
  --express 'instances[0].record("demo-region")' --limit 5

record 返回 void,Arthas 对 void 方法的表达式求值结果通常显示为 null,这是正常的。接着再读字段,确认是不是 +1 了:

vmtool -x 2 --action getInstances \
  --className com.ttk.service.OrderStatsService \
  --express '{instances[0].orderTotal.get(), instances[0].lastRegion}' --limit 5

学会这招之后,第一反应多半和我一样:"拿到活动的对象了,是不是能在生产上直接动手?" 但冷静想了想--光读字段一般没事,调写方法就等于在线上改状态了。 没二次确认、没回滚预案,手别那么快。

可能有人会问:这和 ognl 命令是不是一回事?

都用 OGNL 表达式,但入口不一样。ognl 一般从静态字段、Spring context 这些已知的地方下手;vmtool 是先去堆里把实例搜出来。想先拿到对象再动手,优先 vmtool。

五、Arthas 查 Spring Bean:从 ApplicationContext 入手

排查里另一个常见的场景:明明加了 @Service,运行时却像没注册一样——NoSuchBeanDefinitionException、注入失败,或者你怀疑拿到的是另一个同名 Bean。

ApplicationContext(Spring 应用上下文):Spring 容器的入口,Bean 的注册和查找都走它。拿到上下文实例后,调一下 getBean(...) 就能看某个 Bean 到底在不在容器里。

先确认堆里有没有上下文实例:

vmtool --action getInstances \
  --className org.springframework.context.ApplicationContext \
  --express 'instances.length' --limit 5 -x 0

Spring Boot 进程里一般只有一个 ApplicationContext 实例(如果有父子容器,可能有两个)。确认完捞 Bean:

vmtool --action getInstances \
  --className org.springframework.context.ApplicationContext \
  --express 'instances[0].getBean("orderStatsService")' -x 1 --limit 5

能打出对象,说明容器里确实有;再配合第三节读字段,「Bean 在不在」和「Bean 里现在是什么」就一次说清了。

Fat jar / 多 ClassLoader 的情况下,要是报找不到类,按官方说的补个 --classLoaderClass(比如 org.springframework.boot.loader.LaunchedURLClassLoader),或者先用 sc -d 拿到 classLoaderHash,再用 -c 指定。

六、顺带一提:logger 临时改日志级别

注意logger 不是 vmtool 的功能,是 Arthas 另一条命令。之所以放在这里讲,是因为排查时它和 vmtool 经常搭着用--vmtool 看清状态后,往往还要把日志打细一点确认分支。

线上日志一般是 INFO,可某个类你怀疑它内部走的分支不对,想看 DEBUG 日志,又不想为了这个发一版:

# 先看当前级别
logger --name com.ttk.service.OrderStatsService
# 直接改成 DEBUG,立刻生效,不用重启
logger --name com.ttk.service.OrderStatsService --level debug

这是类级别的改,只影响你指定的那个类,不会把整个应用的日志都打爆。排查完记得改回去,不然线上 DEBUG 日志量会很大。

七、forceGc 和线上的安全边界

vmtool 还有个 --action forceGc,看名字就懂,强制触发一次 GC:

vmtool --action forceGc

forceGc 真正有用的场景是配合 getInstances 验证回收:先记下某个类的实例数,forceGc 之后再查一次,如果实例数没降,说明这些对象还被 GC Root 持有,是排查内存泄漏的重要线索。排查内存问题时,"强制 GC -> 再查实例数"是个很实用的闭环。

forceGc 不是日常排查按钮。 一般只在怀疑内存泄漏、想配合 vmoption 打开 PrintGC 看回收情况的时候才用。生产高峰期随手敲一下 forceGc,等于给自己制造一次停顿,搞不好业务就抖一下。

官方文档里还有 heapAnalyzereferenceAnalyzeinterruptThreadmallocTrimmallocStats 这些动作,都是内存和线程那块的。本篇先把 getInstances 用熟,内存那些留给后面 profiler / 堆分析那几期。

诉求 优先命令 原因
看某次调用的入参/返回值 watch 请求触发,结果直观
看走了哪条分支、哪层最慢 trace 调用树 + 耗时
没请求也要看对象当前字段 vmtool getInstances 直接摸堆内实例
确认 Spring Bean 是否注册 vmtool + getBean 从 ApplicationContext 验证
临时抬高某类日志级别 logger 类级别改 level,不靠发版

八、watch / trace / Arthas vmtool 怎么搭配着用

三期写下来,实战口诀其实就一句:

有请求:先 watch 看结果对不对,再 trace 看走哪条路、哪层慢;没请求,或者想看「现在堆里长啥样」:上 vmtool。

维度 watch trace vmtool
触发方式 方法被调用 方法被调用 立刻查堆
看见什么 入参 / 返回 / 异常 路径 + 耗时 实例字段 / 可调方法
典型痛点 结果不对 分支没走到、接口慢 没请求、字段飘了、Bean 找不到
线上风险 较低(就是看) 中等(有开销) 读字段还好;写方法 / forceGc 风险高

推荐的排查顺序是:先看有没有请求能复现 → 有就 watch/trace → 没有或者要看容器里的状态就上 vmtool → 还想看细节日志再临时改 logger。 别一上来就对生产对象调写方法,出了事没法回滚。

结语

Arthas vmtool 补上的,就是 watch / trace 够不着的那一块:不用等请求,直接去 JVM 里把还活着的实例摸出来。会用 getInstances 读字段、会从 ApplicationContext 验 Bean、知道写方法和 forceGc 要悠着点,大半「线上看不清状态」的情况你都能对付了。

下一篇我会接着写这个系列,往 profiler / 火焰图那边走,专门对付「CPU 热点在哪」这种问题。本文参数以 Arthas 官方 vmtool 文档 为准,版本变了以官网为准。


我是程序员天天困,持续分享编程干货。觉得有用的话记得点赞收藏和关注~也欢迎在评论区聊聊:你有没有遇到过这种情况--明明根本没请求进来,却需要把线上某个对象的状态看清楚?你是怎么排查的?

相关文章
|
3天前
|
人工智能 JSON 安全
|
3天前
|
云安全 人工智能 安全
|
3天前
|
人工智能 自然语言处理 数据挖掘
Qwen3.8-Max-Preview深度全解析:2.4万亿参数旗舰MoE模型+Token Plan限时优惠完整落地指南
2026年7月,全新旗舰级混合专家大模型Qwen3.8-Max-Preview正式开放抢先体验,作为通义千问Qwen3系列规格最高、综合推理能力顶尖的新一代模型,该模型总参数量达到2.4万亿(2.4T),是当前线上可调用的原生多模态旗舰模型,综合推理水准对标海外顶级Fable 5模型,在复杂工程开发、长文档深度分析、多步骤智能体自治、跨境多语言创作、海量数据挖掘五大高难度业务场景实现跨越式性能提升。
709 0
|
3天前
|
人工智能 自然语言处理 数据挖掘
最新版通义千问(Qwen3.8-Max-Preview)功能介绍
2026年,通义千问正式推出全新旗舰级大模型 **Qwen3.8-Max-Preview 预览版**,作为首款突破万亿参数规格的新一代基座模型,该模型总参数量达到**2.4万亿**,采用全新迭代的MoE混合专家架构,综合推理性能、长文本处理、多模态理解、复杂任务规划能力全面超越前代Qwen3.7-Max版本,整体实力跻身全球第一梯队,可对标海外顶级旗舰模型,是当前面向复杂工程开发、多智能体协同、超长文档解析、专业办公自动化场景的最优国产基座模型。
734 0
|
5天前
|
人工智能
Qwen3.8抢先体验!正式版即将发布并开源!
千问Qwen3.8即将开源,参数达2.4T,进化速度以“天”计,实力媲美Fable 5。预览版Qwen3.8-Max已上线阿里Token Plan等平台,限时优惠:日间Credits低至1折,夜间更优,个人/团队版月付仅35元起!
654 25
|
4天前
|
人工智能 测试技术 语音技术
Qwen-Audio-3.0-TTS 正式发布!AI 语音从 “能说话” 升级到 “会带情绪表达”
阿里云发布Qwen-Audio-3.0-TTS语音合成大模型,支持细粒度标签控制(如[gasp][angry])、freestyle自由风格、16种语言及20种方言,声学鲁棒性强。含Flash(首包延时300ms)和Plus(全球榜单冠军)双版本,已在百炼平台开放调用。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
594 1
|
4天前
|
人工智能 自然语言处理 数据挖掘
Qwen3.8-Max 预览版全解析:2.4 万亿参数旗舰模型,Token Plan 限时优惠指南
Qwen3.8-Max-Preview是通义千问Qwen3系列旗舰MoE大模型,参数达2.4万亿,综合推理能力居行业第一梯队。支持思考/快速双模式,擅长大模型五大高难场景。现于阿里云百炼Token Plan、Qoder及QoderWork上线体验,个人版低至39元/月。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
522 1
Qwen3.8-Max 预览版全解析:2.4 万亿参数旗舰模型,Token Plan 限时优惠指南
|
11天前
|
缓存 UED 开发者
Codex109天重置23次,明天还要再送一次
Codex近109天完成23次额度重置,7月14日将迎来第24次。Tibo高频响应用户反馈:优化GPT-5.6高消耗问题、补发失效福利、调整重置时间——形成“反馈→回应→修复→补偿”正向闭环,彰显以用户为中心的产品哲学。(239字)
928 12

热门文章

最新文章