太方便了!Arthas,生产问题大杀器

一、一个难查的生产问题

一天,小王发现生产环境上偶发性地出现某接口耗时过高,但在测试环境又无法复现,小王一筹莫展😔。小王"幻想"到:如果有个工具能记录生产上各个函数的耗时该多好,这样一看不就知道时间花在哪了?

这不是幻想,Arthas 已经帮我们解决了这个问题。在介绍它之前,我们先了解下相关背景。

二、动态追踪

现在互联网和大家生活的各个方面都息息相关。相应地,互联网应用的用户规模也变得越来越大。江湖大了,什么风浪都有。开发者们不断被各种诡异问题打扰,接口耗时过大、CPU 占用过高、内存溢出、只有生产环境会报错 ......

这些问题出现的概率可能是千分之一、乃至万分之一。如果我们能不修改代码、不修改配置、不重启服务 ,就能看到程序内部在执行什么 ,这该多好,再大的问题心里也有底了。

动态追踪技术出现了,它诞生于21 世纪初。Sun Microsystems 公司的工程师在解决一个复杂问题时被繁琐的排查过程所困扰,痛定思痛,他们创造了 DTrace 动态跟踪框架。DTrace 奠定了动态追踪的基础,Bryan Cantrill, Mike Shapiro, and Adam Leventhal 三位作者也多次获得行业荣誉。

动态追踪技术出现的时间早,但 Java 语言相关的调试工具链一直不太完善。直到进入移动互联网时代,Java 的发展才进入了快车道。2018 年,Alibaba 开源 Arthas,Java 的动态追踪才真正好用起来 。

动态追踪可以看作是构建了一个运行时"只读数据库" ,这个数据库内部保存了实时变化的 进程运行信息,我们通过调用这个"数据库"开放的接口,就能看到进程内部发生了什么。

经验丰富的读者可能会有疑问,现在微服务都用上了 Skywalking 这样的分布式链路追踪技术,通过它也能分阶段地看到各个部分的执行情况,为什么还需要 Arthas?

Arthas 有两大特点:

  1. 低侵入;不需要程序中进行额外配置,更不需要手动埋点。
  2. 功能强大;Arthas 提供了四十多种命令:从查看线程调用链,到查看输入、输出,到反编译代码等,应有尽有。

对于排查接口耗时长这样的情况,Skywalking 可以和 Arthas 配合起来,先用 Skywalking 定位出异常微服务,再用 Arthas 分析单个进程的情况,找到根因。

三、Arthas常用场景

相信你对动态追踪有了基本的了解,Arthas 可以理解为动态追踪在 Java 领域落地的具体工具。下面以场景助学,大家可以参考这些方案,因事制宜来解决自己的问题。

Arthas 的安装和基础使用见官方文档:Introduction | arthas。

3.1.接口慢/吞吐量低

在文章开头,小王就遇到了这个问题。现在小王依靠老道的排查经验确定了 MathGame 服务肯定有问题,但具体的点却找不到。小王仔细学习了这篇文章,决定用 Arthas 分以下三步来排查:

  1. profile 明确整体的耗时情况

    profile 命令支持为应用生成火焰图,在 Arthas 终端输入以下命令:

    shell 复制代码
    # 开始对应用中当前执行的活动采样 30 秒,采样结束后默认会生成 HTML 文件
    [arthas@5555]$ profiler start -d 30

    打开 HTML 文件能看到这样的结构:

    火焰图

    MathGame 类 下的 run 方法占用了大部分的执行时间,接下来我们看看 run 方法内部的耗时情况。

  2. trace 详细查看单个调用的内部耗时

    shell 复制代码
    [arthas@5555]$ trace --skipJDKMethod false demo.MathGame run

    PrintStream 类 的 print 方法 占据了 87% 的时间,这是 JDK 自带的类,这说明我们程序本身并无耗时问题,但 MathGame 类的 primeFactors 方法抛出了异常,我们可以看看具体的异常,再思考怎么优化。

    run方法的trace流

    另外,trace 可以选择性地进行调用拦截,比如设置只拦截大于 20ms 的调用:

    shell 复制代码
    [arthas@5555]$ trace demo.MathGame run '#cost > 20'
  3. watch 查看真实的调用数据

    拦截 primeFactors 方法抛出的异常:

    shell 复制代码
    [arthas@5555]$ watch demo.MathGame primeFactors -e "throwExp"

    拦截异常

小王从大到小、逐步分析,找出了问题的原因是 primeFactors 抛出了异常,修正参数后,程序恢复了正常。

3.2.CPU 占用过高

CPU 是程序运行的核心计算资源,一旦出现 CPU 占用过高,必定对大部分用户的访问耗时产生影响。针对这类问题,要定位出有问题的线程,并获取该线程当前执行的代码位置 。

使用 top + jstack 命令可以定位这类问题(见参考资料三),Arthas 也提供了更便捷的一体化工具:

  1. 定位目标线程

    shell 复制代码
    # 调用线程看板,并刷新数据三次
    [arthas@5555]$ dashboard -n 3

    示例程序的CPU占用不算高

    DashBoard 刷新三次后,在最新状态中发现示例程序里自己的线程 "main" 占用不算高。说明程序运行正常。如果是要排错,这里就要找出 CPU 占用最高的用户线程的 ID。

  2. 查看目标线程执行的代码位置

    shell 复制代码
    # "1" 是上一步定位到的 main 的线程ID
    [arthas@5555]$ thread 1


    线程正在"睡觉",没什么大问题。

3.3 生产环境的效果和测试不一样

有些时候你发现:测试环境正常,但生产就报错了。这类问题主要靠做好上线流程的管控,但也有可能是打包的依赖库出现冲突,造成程序行为不一致。接下来,我们看看怎么用 Arthas 反编译代码,以及怎么对比依赖库的版本。

  1. 反编译代码

    shell 复制代码
    # demo.MathGame 是目标类的全限定名
    [arthas@5555]$ jad demo.MathGame
  1. 查看目标类所属的依赖包

    shell 复制代码
    # demo.MathGame 是目标类的全限定名
    [arthas@5555]$ sc -d demo.MathGame

    目标类所属的包
    如果这里是依赖包,code-source 还可以显示所属包的版本。这样就可以对比本地的代码,从而在打包时设置正确的依赖版本。

3.4 内存溢出

生产问题中内存溢出也有不小的比例。内存溢出的关键是找出高内存占用的对象。命令行操作会比较麻烦,建议转储 Heap Dump 等文件后,通过 Eclipse Memory Analyzer(MAT) 等工具进行分析。

四、运行 Arthas 报错

在有些运行环境下,Arthas 会出现报错。对于以下两种情况,读者可参照文档解决:

五、参考资料

  1. 动态追踪技术漫谈 - OpenResty 官方博客
  2. DTrace - Wikipedia --- DTrace - 维基百科,自由的百科全书
  3. 用JStack和Top分析Java进程CPU占用率_java top threads-CSDN博客
  4. Introduction | arthas --- 简介 |阿尔萨斯 (aliyun.com)
  5. 如何读懂火焰图? - 阮一峰的网络日志 (ruanyifeng.com)
相关推荐
SimonKing几秒前
DeepSeek 新模型免费白嫖,夯夯夯,赶紧用起来
java·后端·程序员
打工仔折腾 AI12 分钟前
介入式脑机接口的跨尺度解码与闭环调控:从算法落地到后端工程实践
人工智能·后端·python·深度学习·算法·ai agent 实战
SabrinaW71112 分钟前
基于 C++17 与 RTTR 的注册式对象工厂:配置驱动的动态对象创建
java·前端·数据库
喵个咪15 分钟前
GoWind Ops 深度拆解:纯出站 Agent + MQTT 信令,把「管机器、跑项目、发脚本、构建分发」做扎实
后端·go·自动化运维
zhangx1234_26 分钟前
javaEE 多线程4
java·开发语言
鸠摩智首席音效师27 分钟前
如何清理 ASP.NET 临时文件 ?
后端·asp.net
铁皮饭盒27 分钟前
刷到 FDE 岗位那一刻,我还以为前端又活了
前端·后端
利威尔xu1 小时前
Go GMP 调度器 10 个高频追问:3 个线上故障让 P99 从 80ms 飙到 2s、10 万 goroutine 泄漏
开发语言·后端·面试·性能优化·golang
中电金信1 小时前
从业务调优到分层缓存:DeepSeek V4 Flash的PPU实践
java·开发语言