news 2026/9/19 17:08:04

Arthas tt 命令深度指南:用 TimeTunnel 时间隧道回溯、检索与重放方法调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Arthas tt 命令深度指南:用 TimeTunnel 时间隧道回溯、检索与重放方法调用

Arthas tt 命令深度指南:用 TimeTunnel 时间隧道回溯、检索与重放方法调用

【免费下载链接】arthasAlibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas项目地址: https://gitcode.com/gh_mirrors/ar/arthas

tt(Time Tunnel,时间隧道)是 Arthas 中极具特色的一条命令:它能够将目标方法每次调用的入参、返回值、抛出的异常、调用耗时与调用对象等完整上下文快照成一个个"时间片段"(Time Fragment)缓存下来,随后你可以随时按索引查看某次调用的完整细节、用 OGNL 表达式检索或观察记录、甚至用当时的参数原样重放那次调用。本指南以tt命令为主线,结合 Arthas 源码讲解其实现原理、全部参数与实战用法,读完你即可在线上问题排查中完成"记录调用 → 检索命中 → 查看细节 → 重放复现"的完整闭环。

为什么需要 tt:watch之外的时光机

watch虽然强大,但存在固有的局限:它需要你在方法被调用守在那里观察输出,且受限于观测时机和表达式的复杂度,面对"偶发异常、难以现场复现"的问题往往力不从心——异常可能在你反应过来之前就已经过去了。

tt正是为此而生。它把每一次方法调用记录下来(默认最多 100 条),相当于给业务方法装了一台"黑匣子"。问题发生后,你不需要等待下一次触发,而是直接回到历史记录中,查看当时发生了什么:参数是什么、返回了什么、抛了什么异常、耗时多久、调用对象是谁。更进一步,你还可以把某次失败的调用"重放"一遍,用当时的入参重新执行该方法,观察问题能否稳定复现。

从源码结构看,tt的实现位于 TimeTunnelCommand.java,类注释即点明了它的本质——"时光隧道命令"。

工作原理与核心数据模型

要熟练使用tt,先理解它的底层模型。

时间片段(TimeFragment)

每次被增强方法的一次调用,Arthas 会封装成一个TimeFragment,其定义在 TimeFragment.java:

class TimeFragment { private final Advice advice; // 调用上下文:入参、返回值、异常、目标对象、类与方法 private final LocalDateTime gmtCreate; // 调用发生时间 private final double cost; // 调用耗时(毫秒) }

其中Advice(通知上下文)携带了关键数据,接口定义在 Advice.java,包括:

  • getTarget():调用该方法的实例对象
  • getParams():方法入参数组
  • getReturnObj():正常返回时的返回值
  • getThrowExp():抛出异常时的异常对象
  • isAfterReturning()/isAfterThrowing():本次调用是正常返回还是异常抛出
  • getLoader():目标类对应的 ClassLoader

时间片段容器

所有TimeFragment存放于一个静态LinkedHashMap<Integer, TimeFragment>中(timeFragmentMap),键为递增的时间片段索引;索引由一个从1000开始的AtomicInteger序列生成器分配(见TimeTunnelCommand中的sequence字段),这就是为什么你看到的第一条记录 INDEX 总是 1000。按文档说明,该 Map 的默认容量为 100。

记录时机

记录动作由增强后的监听器完成,实现在 TimeTunnelAdviceListener.java:

  • 方法执行前(before):通过pushArgs(args)保存入参时的参数快照,并启动耗时计时;
  • 方法正常返回(afterReturning)或抛出异常(afterThrowing):取出入参快照、计算耗时,封装为TimeFragment存入容器,并输出一行表格。

一个值得注意的实现细节:before阶段保存的入参是原始引用快照afterReturning/afterThrowing阶段会用popArgs()取回它——源码注释明确写道"取出入参时的 args,因为在函数执行过程中 args 可能被修改"。这正是下文"注意事项"中参数可能被业务代码修改这一限制的对策:tt 记录的是进入方法那一刻的参数。

两个必须牢记的预防措施(Precautions)

  • 容量有限tt的实现是把函数入参/返回值保存进一个Map<Integer, TimeFragment>,默认大小 100。记录达到上限后,新记录会覆盖旧记录。
  • 手动释放内存:使用tt相关功能后,必须手动释放内存(见下文删除/清空小节),否则长时间运行可能发生 OOM;退出 Arthas 并不会自动清空 tt 的缓存 Map(它挂在静态字段上),这一点务必在生产上注意。

环境准备:启动 math-game 示例

本指南的所有示例围绕仓库内的示例程序math-game展开,其源码在 MathGame.java:程序每秒循环执行run(),内部随机生成一个数并调用primeFactors(int number)做质因数分解;当number < 2时抛IllegalArgumentException("number is: ... , need >= 2")——正是我们观察异常时间片段的理想目标。

启动方式请参考仓库文档 Quick Start,或直接:

# 先启动 math-game(一个持续运行的 Java 进程) java -jar math-game.jar # 再启动 Arthas 并 attach 到该进程 ./as.sh

记录方法调用:tt -t

-t--time-tunnel)是核心的"记录"开关,用于增强目标方法并开始录制:

$ tt -t demo.MathGame primeFactors Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 66 ms. INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ------------------------------------------------------------------------------------------------------------------------------------- 1000 2018-12-04 11:15:38 1.096236 false true 0x4b67cf4d MathGame primeFactors 1001 2018-12-04 11:15:39 0.191848 false true 0x4b67cf4d MathGame primeFactors 1002 2018-12-04 11:15:40 0.069523 false true 0x4b67cf4d MathGame primeFactors 1003 2018-12-04 11:15:41 0.186073 false true 0x4b67cf4d MathGame primeFactors 1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors

命令语法为tt -t <class-pattern> <method-pattern> [condition-express]。记录期间可随时按Ctrl+C(或Q)停止增强。

控制记录规模:-n-m

$ tt -t -m 1 demo.MathGame primeFactors Press Q or Ctrl+C to abort. Affect(class count:1 , method count:1) cost in 130 ms, listenerId: 1. INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ------------------------------------------------------------------------------------------------------------------------------------- 1000 2022-12-25 19:41:45 2.629929 true false 0x3bf400 MathGame primeFactors 1001 2022-12-25 19:41:55 0.146161 false true 0x3bf400 MathGame primeFactors
  • -t:记录demo.MathGame primeFactors的调用上下文。
  • -n <N>--limits:限制记录条数,避免记录过多导致溢出。配合-n时,Arthas 会在记录数达到指定上限后自动停止记录(源码中由isLimitExceeded触发abortProcess,见 TimeTunnelAdviceListener.java)。默认值为 100。
  • -m <N>:限制匹配到的 Class 数量,避免匹配到过多类导致 JVM 挂起,默认值 50。
  • -c <classloader hash>--classloader:当同一个类被多个 ClassLoader 加载时,可用-c只增强指定 ClassLoader 下的类。用sc -d <className>可以查到对应的 classloader hash。
  • -E--regex:启用正则表达式匹配(默认是通配符匹配)。

记录表格属性说明

名称说明
INDEX按时间递增的调用索引
TIMESTAMP方法调用的时间
COST(ms)该方法调用的耗时(毫秒)
IS-RET方法是否正常返回
IS-EXP方法是否抛出异常
OBJECT调用该方法对象的hashCode()(十六进制)
CLASS调用该方法的对象所属类名
METHOD被调用的方法名

条件表达式:只记录关心的调用

tt -t的第三个位置参数是条件表达式(condition-express),只有表达式求值为true的调用才会被记录。常用技巧(来自官方文档 Tips):

  1. 按参数个数筛选:tt -t *Test print params.length==1(匹配只有一个参数的方法调用);
  2. 按参数类型筛选:tt -t *Test print 'params[1] instanceof Integer'
  3. 按指定参数值筛选:tt -t *Test print params[0].mobile=="13989838402"

表达式中可使用的字段即 fundamental fields in expressions 中定义的核心变量(如paramstargetreturnObjthrowExpclazzmethod等),并支持 OGNL 的完整语法能力。当表达式异常时,TimeTunnelAdviceListener.afterFinishing会中止记录并输出失败原因。

列出所有记录:tt -l

$ tt -l INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ------------------------------------------------------------------------------------------------------------------------------------- 1000 2018-12-04 11:15:38 1.096236 false true 0x4b67cf4d MathGame primeFactors 1001 2018-12-04 11:15:39 0.191848 false true 0x4b67cf4d MathGame primeFactors 1002 2018-12-04 11:15:40 0.069523 false true 0x4b67cf4d MathGame primeFactors 1003 2018-12-04 11:15:41 0.186073 false true 0x4b67cf4d MathGame primeFactors 1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors 9 1005 2018-12-04 11:15:43 0.4776 false true 0x4b67cf4d MathGame primeFactors Affect(row-cnt:6) cost in 4 ms.

tt -l按时间顺序展示全部时间片段。注意表格中那一行孤立的9:当相邻记录的部分列值相同(这里是 OBJECT 列)时,Arthas 会用"合并占位"的方式简化展示。源码中对应processList分支(见 TimeTunnelCommand.java),它把整个timeFragmentMap渲染为TimeFragmentVO列表输出。

搜索记录:tt -s

记录多了之后,可以用-s--search-express)按 OGNL 表达式过滤出符合条件的记录。表达式结构与条件表达式一致,同样基于Advice的字段:

$ tt -s 'method.name=="primeFactors"' INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ------------------------------------------------------------------------------------------------------------------------------------- 1000 2018-12-04 11:15:38 1.096236 false true 0x4b67cf4d MathGame primeFactors 1001 2018-12-04 11:15:39 0.191848 false true 0x4b67cf4d MathGame primeFactors 1002 2018-12-04 11:15:40 0.069523 false true 0x4b67cf4d MathGame primeFactors 1003 2018-12-04 11:15:41 0.186073 false true 0x4b67cf4d MathGame primeFactors 1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors 9 1005 2018-12-04 11:15:43 0.4776 false true 0x4b67cf4d MathGame primeFactors Affect(row-cnt:6) cost in 607 ms.

从实现上看,processSearch会遍历timeFragmentMap的每一条记录,用ExpressFactory.threadLocalExpress(advice).is(searchExpress)求值过滤(见 TimeTunnelCommand.java)。更进阶的用法是tt -s '<搜索表达式>' -w '<观察表达式>'组合:先筛出命中的记录,再对每条命中记录求观察表达式并输出结果(对应源码hasWatchExpress()分支)。

表达式支持的核心字段参见 Critical fields in expression。

查看调用上下文:tt -i <index>

tt -i按索引查看某一次调用的完整细节,包括参数对象内容、返回值或异常堆栈:

$ tt -i 1003 INDEX 1003 GMT-CREATE 2018-12-04 11:15:41 COST(ms) 0.186073 OBJECT 0x4b67cf4d CLASS demo.MathGame METHOD primeFactors IS-RETURN false IS-EXCEPTION true PARAMETERS[0] @Integer[-564322413] THROW-EXCEPTION java.lang.IllegalArgumentException: number is: -564322413, need >= 2 at demo.MathGame.primeFactors(MathGame.java:46) at demo.MathGame.run(MathGame.java:24) at demo.MathGame.main(MathGame.java:16) Affect(row-cnt:1) cost in 11 ms.

这条输出完美对应了 MathGame.java 中primeFactorsnumber < 2抛异常的逻辑:PARAMETERS[0]-564322413,异常信息提示 "number is: -564322413, need >= 2"。通过对比多个tt -i的结果,你就能定位"为什么有的请求失败、有的成功"。

若索引不存在,命令会提示Time fragment[<index>] does not exist.(源码processShow分支)。

重放记录:tt -i <index> -p

这是tt最独特的杀手锏:因为时间片段保存了完整的调用上下文(目标对象 + 入参),Arthas 可以事后用当时的参数再次执行该方法,用于高级问题的复现调试:

$ tt -i 1004 -p RE-INDEX 1004 GMT-REPLAY 2018-12-04 11:26:00 OBJECT 0x4b67cf4d CLASS demo.MathGame METHOD primeFactors PARAMETERS[0] @Integer[946738738] IS-RETURN true IS-EXCEPTION false RETURN-OBJ @ArrayList[ @Integer[2], @Integer[11], @Integer[17], @Integer[2531387], ] Time fragment[1004] successfully replayed. Affect(row-cnt:1) cost in 14 ms.
  • -p--play:开启重放;
  • --replay-times <N>:重放执行次数(默认 1);
  • --replay-interval <ms>:多次重放之间的间隔,单位毫秒,默认 1000。

从源码看,重放的实现(processPlay,见 TimeTunnelCommand.java)是:取出记录的Advice,通过反射method.invoke(advice.getTarget(), advice.getParams())记录时的参数重新调用目标方法,并重新统计耗时与返回/异常结果。可见tt重放的是"原方法调用",而不是复现字节码执行,因此重放结果反映的是当前时刻的代码行为。

观察表达式:tt -w-x

-w--watch-express)用 OGNL 表达式观察某个时间片段,表达式内可使用 fundamental fields in expressions 中定义的全部变量(paramstargetreturnObjthrowExp等)。-x--expand)控制对象的展开层级,默认 1。

先记录并取一个索引,再观察目标对象的字段:

[arthas@10718]$ tt -t demo.MathGame run -n 5 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1 INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1000 2021-01-08 21:54:17 0.901091 true false 0x7699a589 MathGame run [arthas@10718]$ tt -w 'target.illegalArgumentCount' -x 1 -i 1000 @Integer[60] Affect(row-cnt:1) cost in 7 ms.

示例中target.illegalArgumentCount读取了 MathGame.java 中定义的实例字段illegalArgumentCount——通过它你可以观察调用对象的内部状态变化。

获取静态字段、调用静态方法

由于tt -w走 OGNL 表达式,你也可以访问静态成员:

[arthas@10718]$ tt -t demo.MathGame run -n 5 Press Q or Ctrl+C to abort. Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1 INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1000 2021-01-08 21:54:17 0.901091 true false 0x7699a589 MathGame run [arthas@10718]$ tt -w '@demo.MathGame@random.nextInt(100)' -x 1 -i 1000 @Integer[46]

这里用@demo.MathGame@random.nextInt(100)访问了 MathGame.java 中的静态字段random并调用了它的nextInt(100)

关于 ClassLoader 的注意点:表达式求值内部使用的是com.taobao.arthas.core.advisor.Advice#getLoader返回的 ClassLoader(见 Advice.java)。若目标方法在自定义 ClassLoader 下加载,建议配合 ognl 命令 使用精确的 ClassLoader,避免因 ClassLoader 隔离导致表达式访问不到目标类。若需要通过获取 Spring 上下文调用 Bean 方法做更深入的观察,可参考官方 issue #482 中给出的思路。

注意事项(F.Y.I):使用tt前必须了解的限制

  1. ThreadLocal会丢失:Arthas 把入参保存到数组后,重放时会在另一个线程中再次调用该方法,因此方法执行期间依赖的ThreadLocal在重放场景下会丢失,涉及线程上下文(如 traceId、用户态信息)的方法重放结果可能失真。
  2. 参数可能被修改:保存到数组里的是对象引用,而非深拷贝。这些对象可能被其他业务代码修改,导致之后查看/重放时看到的是"变了形"的参数。作为缓解,监听器在before阶段会保存入参时的快照(见上文源码解析),记录的是方法进入瞬间的参数。
  3. 内存占用:时间片段持有目标对象与参数对象的强引用,记录长期不清理会积累内存,务必在排查结束后用删除/清空命令释放。

删除与清理:tt -dtt --delete-all

按索引删除单条记录:

tt -d -i 1001

清空全部记录:

tt --delete-all

对应源码实现:processDeletetimeFragmentMapremove(index)并输出 "Time fragment[ ] successfully deleted.";processDeleteAll直接clear()整个 Map 并输出 "Time fragments are cleaned."(见 TimeTunnelCommand.java)。由于该 Map 是静态的、退出 Arthas 也不会自动清理,建议在每次tt排查结束后主动执行tt --delete-all,这是避免长时间运行 OOM 的关键习惯。

参数速查表

参数长选项说明默认值
-t--time-tunnel记录方法调用为时间片段关闭
-l--list列出全部时间片段关闭
-i <index>--index查看/操作指定索引的时间片段
-s <expr>--search-express按 OGNL 表达式搜索记录
-w <expr>--watch-express对指定记录求 OGNL 观察表达式
-p--play重放指定索引的记录关闭
--replay-times <N>-重放次数1
--replay-interval <ms>-重放间隔(毫秒)1000
-n <N>--limits记录条数上限,达到后自动停止100
-m <N>-匹配类数量上限,避免 JVM 挂起50
-c <hash>--classloader只增强指定 ClassLoader 下的类
-E--regex启用正则匹配类/方法名关闭
-x <N>--expand结果对象的展开层级1
-d--delete删除指定索引的记录(需配合-i关闭
--delete-all-清空全部记录关闭
-M <bytes>--sizeLimit结果对象大小上限(字节,需大于 0)来自 options object-size-limit

总结:tt 的典型排查流程

  1. tt -t <Class> <method> [-n 100] [condition-express]开启记录,按条件只捕获你关心的调用;
  2. 问题发生后tt -l概览、tt -s '<expr>'精确筛出问题记录;
  3. tt -i <index>查看该次调用的参数、返回值与异常堆栈,结合 MathGame.java 这类业务代码对照分析;
  4. 需要复现时tt -i <index> -p [--replay-times N] [--replay-interval ms]用历史参数重放;
  5. tt -w '<expr>' -x 1 -i <index>深入观察对象内部状态或调用静态方法;
  6. 排查结束后tt --delete-all释放内存。

tt与 watch、stack、trace 等命令互为补充:watch/trace适合实时观测,而tt的价值在于"事后取证"与"历史重放",是线上偶发问题定位中不可替代的一环。更多命令级用法可参阅仓库文档 commands 与 arthas3。

【免费下载链接】arthasAlibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas项目地址: https://gitcode.com/gh_mirrors/ar/arthas

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 17:07:37

免费 OSINT 工具 Blackbird:快速完成 600+ 平台账号搜索

免费 OSINT 工具 Blackbird&#xff1a;快速完成 600 平台账号搜索 【免费下载链接】blackbird An OSINT tool to search for accounts by username and email in social networks. 项目地址: https://gitcode.com/GitHub_Trending/bl/blackbird Blackbird 是一款免费的…

作者头像 李华
网站建设 2026/9/19 17:04:42

2023数据中台建设方案:DataOps驱动的MVP落地实践

简介&#xff1a;本资源为一份面向企业数字化转型实践者、数据架构师与中台建设团队的2023年数据中台项目建设方案&#xff0c;聚焦解决多源数据分散、治理低效、指标口径不一、资产价值难量化等典型痛点。方案全文以Word文档&#xff08;.docx&#xff09;形式呈现&#xff0c…

作者头像 李华
网站建设 2026/9/19 17:02:19

DeepSeek 降重版测评,论文改写的 Base URL 填 TaoToken 的 API 地址

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 17:00:20

社区健康志愿服务APP开发:FastAPI订单状态机与GPS签到实践

简介&#xff1a;“乐助”社区健康志愿服务APP平台的设计与构建是一篇聚焦社区志愿管理数字化的学术论文&#xff0c;面向健康服务、护理信息化及APP开发方向的从业者和研究者。论文以沈阳市5个社区180名居民为调查对象&#xff0c;采用自研问卷得出居民参与意愿达80.9%、对志愿…

作者头像 李华