文章目录

Arthas诊断工具

前言

简介:由于在项目中遇到一种情况,某段代码在进行单元测试和在 tomcat 容器中运行的性能相差数百倍,因此需要分析在不同环境下某个方法执行的具体时间,从而确定问题。Arthas 可以做到无侵入的监控应用远行情况。 — 作者 | agmtopy

Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。

当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到JVM的实时运行状态?
  7. 怎么快速定位应用的热点,生成火焰图?
  8. 怎样直接从JVM内查找某个类的实例?

环境准备

  • Arthas下载地址:

https://arthas.aliyun.com/arthas-boot.jar

Releases · alibaba/arthas (github.com)

  • 测试代码:
package com.xwder.jvm;

import cn.hutool.json.JSONUtil;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 更多资源参考: https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkxNDI0ODE0NQ==&action=getalbum&album_id=1896318852766973955&scene=173&from_msgid=2247483935&from_itemidx=1&count=3&nolastread=1#wechat_redirect
 * arthas测试 参考: https://mp.weixin.qq.com/s/aUy7-90cxOukuFSiwEeA4A
 * 测试过程命令参考:
 * watch com.xwder.jvm.ArthasDemo convert "{params,target,returnObj}" -f -x 4
 * tt -t com.xwder.jvm.ArthasDemo convert "{params,target,returnObj}" -f -x 4
 *
 * jad --source-only com.xwder.jvm.ArthasDemo
 *
 * redefine D:/ArthasDemo.class
 * redefine D:/ArthasDemo$People.class
 *
 * tt -t  com.xwder.jvm.ArthasDemo convert | tee D:/ArthasDemo/log
 * monitor -c 30  com.xwder.jvm.ArthasDemo convert | tee D:/ArthasDemo/log1
 *
 * @author xwder
 */
public class ArthasDemo {
    public static void main(String[] args) {
        String s = "[{\"name\":\"zhangsan\",\"age\":\"10\",\"telephone\":\"123456\",\"interests\":[\"sing\",\"dance\",\"rap\"]},\n" +
                "{\"name\":\"lisi\",\"age\":\"20\",\"telephone\":\"123457\",\"interests\":[\"sing\",\"swim\"]},\n" +
                "{\"name\":\"wangwu\",\"age\":\"30\",\"telephone\":\"123458\",\"interests\":[\"sing\",\"program\"]}]";
        //模拟一遍遍的调用方法的过程
        for (; ; ) {
            System.out.println(new ArthasDemo().convert(s));
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private List<People> convert(String s) {
        return JSONUtil.toList(s, People.class);
    }

    @Getter
    @Setter
    @ToString
    @FieldDefaults(level = AccessLevel.PRIVATE)
    private static class People {
        /**
         * 姓名
         */
        String name;
        /**
         * 年龄
         */
        String age;
        /**
         * 电话
         */
        String telephone;
        /**
         * 兴趣列表
         */
        List<String> interests;
    }
}

启动Arthas

首先运行我们的程序,程序不停输出内容:

"C:\Program Files\Java\jdk-ms-11.0.10+9\bin\java.exe" "-javaagent:D:\Program Files (x86)\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar=54326:D:\Program Files (x86)\JetBrains\IntelliJ IDEA 2021.1.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\xwderWorkSpace\xwder-tech\java-knowledge-summary\target\classes;D:\Program Files (x86)\mavenrepository\org\projectlombok\lombok\1.18.8\lombok-1.18.8.jar;D:\Program Files (x86)\mavenrepository\junit\junit\4.13.2\junit-4.13.2.jar;D:\Program Files (x86)\mavenrepository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\Program Files (x86)\mavenrepository\cn\hutool\hutool-all\5.6.6\hutool-all-5.6.6.jar;D:\Program Files (x86)\mavenrepository\io\projectreactor\reactor-core\3.2.3.RELEASE\reactor-core-3.2.3.RELEASE.jar;D:\Program Files (x86)\mavenrepository\org\reactivestreams\reactive-streams\1.0.2\reactive-streams-1.0.2.jar" com.xwder.jvm.ArthasDemo
[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]
[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]
[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]
[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]

启动命令:java -jar arthas-boot.jar

D:\>java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.5.3
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 37712
  [2]: 43808 org.jetbrains.jps.cmdline.Launcher
  [3]: 15272 org.jetbrains.idea.maven.server.RemoteMavenServer36
  [4]: 15480 org.jetbrains.idea.maven.server.RemoteMavenServer36
  [5]: 25704 org.jetbrains.jps.cmdline.Launcher
  [6]: 35816 com.xwder.jvm.ArthasDemo

然后选择我们测试的程序Java进程:6 attach。

D:\>java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.5.3
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 37712
  [2]: 43808 org.jetbrains.jps.cmdline.Launcher
  [3]: 15272 org.jetbrains.idea.maven.server.RemoteMavenServer36
  [4]: 15480 org.jetbrains.idea.maven.server.RemoteMavenServer36
  [5]: 25704 org.jetbrains.jps.cmdline.Launcher
  [6]: 35816 com.xwder.jvm.ArthasDemo
6
[INFO] arthas home: C:\Users\xwder\.arthas\lib\3.5.3\arthas
[INFO] Try to attach process 35816
[INFO] Attach process 35816 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'


wiki       https://arthas.aliyun.com/doc
tutorials  https://arthas.aliyun.com/doc/arthas-tutorials.html
version    3.5.3
main_class
pid        35816
time       2021-08-12 10:51:26

attach 成功后可以打开谷歌浏览器输入http://127.0.0.1:3658/ 打开 WebConsole

你也可以使用Linux终端管理软件连接Arthas,比如secureCRT,注意连接端口3658,协议Telnet

相关问题测试验证

问题 1:这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?

这个问题我经常在处理各种「依赖冲突」的时候遇到,有一些类的完全名称是一模一样,通过常规的办法无法解决类具体从哪个 jar 包加载。

  • sc命令

通过 sc 命令 模糊查看当前 JVM 中是否加载了包含关键字的类,以及获取其完全名称。

注意使用 sc -d 命令,获取 classLoaderHash,这个值在后面需要用到。

  • classloader命令

通过 classloader 查看 class 文件来自哪个 jar 包

使用 cls 命令可以清空命令行,这个简单的命令官方文档居然找不到。。。

注意 classloader -c 后面的值填上面第一步中获取到的 Hash 值,class 文件路径使用’/‘分割,且必须以.class 结尾。

[arthas@35816]$ classloader -c 1f89ab83 -r com/xwder/jvm/ArthasDemo.class
 file:/D:/xwderWorkSpace/xwder-tech/java-knowledge-summary/target/classes/com/xwder/jvm/ArthasDemo.class      Affect(row-cnt:1) cost in 1 ms.
[arthas@35816]$

上面是显示 class 文件路径的,如果 class 文件来自 jar 包,可以显示 jar 包路径

问题 2:我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

  • watch和 tt 命令

这两个命令都是用来查看方法调用过程的,不同的是 watch 命令是调用一次打印一次方法的调用情况,而 tt 命令可以先生成一个不断增加的调用列表,然后指定其中某一项进行观测。

  1. 使用 watch 命令查看方法调用情况。我们要查看 ArthasDemo 这个类里面的 convert 方法调用情况。
watch com.xwder.jvm.ArthasDemo convert "{params,target,returnObj}" -f -x 4
tt -t com.xwder.jvm.ArthasDemo convert

watch 后面跟上完全类名和方法名,以及一个 OGNL 的表达式,-f 表示不论正常返回还是异常返回都进行观察,-x 表示输出结果的属性遍历深度,默认为 1,建议无脑写 4 就行,这是笔者经验来看最大的遍历深度,再大就不支持了

​ 2.使用 tt 命令来观测方法调用情况,tt 命令可以查看「多次调用」并选择其中一个进行观测,但是如果输出结果是多层嵌套就没办法看了,而 watch 可以查看「多层嵌套」的结果。使用 tt -t 记录下当前方法的每次调用环境现场

  • jad —source-only 命令
  1. 如何判断代码是否已经提交?

通过 jad --source-only 可以查看源代码。

问题 3:遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

  • redefine命令

通过上面问题 2 的 watchtt 命令可以查看方法调用情况。

此外,可以通过 redefine 命令「热替换」线上的代码,注意应用重启之后会失效,这在某些紧急情况下会有奇效。

比如说我们修改一下方法体里面的代码,加了一行日志打印:

private List<People> convert(String s) {
    System.out.println(s);
    return JSONUtil.toList(s, People.class);
}

这时我们就可以将新代码编译后的 class 文件热替换正在运行的 ArthasDemo 的代码。

[arthas@43420]$ redefine D:/ArthasDemo.class
redefine D:/ArthasDemo$People.classredefine success, size: 1, classes:
com.xwder.jvm.ArthasDemo
[arthas@43420]$ redefine D:/ArthasDemo$People.class
redefine success, size: 1, classes:
com.xwder.jvm.ArthasDemo$People
[arthas@43420]$

成功修改线上正在运行的代码,即可生效

问题 4:线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

  • tee、monitor 命令

这个问题没有完美的解决办法

参考一下问题 2 和问题 3的解决方案

推荐使用 tt 命令并将命令行返回结果输出到一个文件中,后续可以选择异常的一行记录使用 tt -i 命令进行深入的分析。

tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。

tt -t  com.xwder.jvm.ArthasDemo convert | tee D:/ArthasDemo/log
monitor -c 30  com.xwder.jvm.ArthasDemo convert | tee D:/ArthasDemo/log1

monitor 命令统计方法调用成功失败情况。

-c 后面接统计周期,默认值为120秒

学习自: 带着8个问题5分钟教你学会Arthas诊断工具)

更多参考:Arthas专题

xwder : 6666
2021-08-15 02:23:59.0