Arthas 看了官方文档还不会用?进来了解一哈~

JVM 专栏收录该内容
65 篇文章 10 订阅

前言

本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构和文献引用请见100个问题搞定Java虚拟机

引子

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

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

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

小明打开了Artha的官方文档 Arthas 用户文档 — Arthas 3.5.2 文档 (aliyun.com)

看到的第一段话就是这个

于是开始吭哧吭哧的读文档

费劲巴拉的读完以后,小明发现上面的问题官方居然没有给出标准的解决方案

image.png

这不是管杀不管埋吗!!!

image.png

正文

下面是笔者结合多年使用 Arthas 的经验,针对这 8 个问题给出的详细解决方案,如果有疑问欢迎评论区指出。

准备

先给出我的测试代码

package com.shockang.study;

import com.alibaba.fastjson.JSON;
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;

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 JSON.parseArray(s, People.class);
    }


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

以下是控制台正常打印的结果

/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/bin/java ...
[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])]

下载并运行 Arthas,选择一个 Java 进程进行 attach

下载并运行Arthas

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

(吐槽一句 Mac OS 的Safari 浏览器不支持)

使用 WebConsole 最方便的是你可以打开多个标签页同时操作

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

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

别急,看我下面的解决办法。

  1. sc

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

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

sc -d *ArthasDemo*

sc-d命令

  1. classloader

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

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

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

[arthas@3633]$ classloader -c 18b4aac2 -r com/shockang/study/ArthasDemo.class
file:/Users/shockang/code/concurrentbook/target/classes/com/shockang/study/ArthasDemo.class
Affect(row-cnt:1) cost in 0 ms.

上面是显示 class 文件路径的,如果 class 文件来自 jar 包,可以显示 jar 包路径,例如官方文档给的例子:

$ classloader -c 1b6d3586 -r java/lang/String.class
jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/String.class

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

推荐使用 watchtt 命令,非常好用。

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

  1. 使用 watch 命令查看方法调用情况。我们要查看 ArthasDemo 这个类里面的 convert 方法调用情况。

watch命令

watch com.shockang.study.ArthasDemo convert "{params,target,returnObj}" -f -x 4

watch 后面跟上完全类名和方法名,以及一个 OGNL 的表达式,-f 表示不论正常返回还是异常返回都进行观察,-x 表示输出结果的属性遍历深度,默认为 1,

建议无脑写 4 就行,这是笔者经验来看最大的遍历深度,再大就不支持了

  1. 使用 tt 命令来观测方法调用情况,tt 命令可以查看多次调用并选择其中一个进行观测,但是如果输出结果是多层嵌套就没办法看了,而 watch 可以查看多层嵌套的结果。

使用 tt -t 记录下当前方法的每次调用环境现场

在这里插入图片描述

tt -t com.shockang.study.ArthasDemo convert

TIMESTAMP表示方法调用发生的时间,COST 表示调用耗时(ms),IS-RET表示是否正常返回,IS-EXP 表示是否异常返回,OBJECT 表示对象的 HASH 值

对于具体一个时间片的信息而言,你可以通过 -i 参数后边跟着对应的 INDEX 编号查看到他的详细信息
tt-i命令

图中之所以可以打印兴趣列表,是调用了其 toString 方法,如果没有重写 java.lang.Object 类的 toString 方法,只会看到 hash 值。

  1. 如何判断代码是否已经提交?

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

[arthas@3633]$ jad --source-only com.shockang.study.ArthasDemo
       /*
        * Decompiled with CFR.
        */
       package com.shockang.study;

       import com.alibaba.fastjson.JSON;
       import java.util.List;
       import java.util.concurrent.TimeUnit;

       public class ArthasDemo {
           public static void main(String[] args) {
/*15*/         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\"]}]";
               while (true) {
/*20*/             System.out.println(new ArthasDemo().convert(s));
                   try {
/*22*/                 TimeUnit.SECONDS.sleep(10L);
/*25*/                 continue;
                   }
                   catch (InterruptedException e) {
/*24*/                 e.printStackTrace();
                       continue;
                   }
                   break;
               }
           }

           private List<People> convert(String s) {
/*30*/         return JSON.parseArray(s, People.class);
           }

           private static class People {
               private String name;
               private String age;
               private String telephone;
               private List<String> interests;

               private People() {
               }

               public String toString() {
                   return "ArthasDemo.People(name=" + this.getName() + ", age=" + this.getAge() + ", telephone=" + this.getTelephone() + ", interests=" + this.getIntere
sts() + ")";
               }

               public String getName() {
                   return this.name;
               }

               public void setName(String name) {
                   this.name = name;
               }

               public String getAge() {
                   return this.age;
               }

               public String getTelephone() {
                   return this.telephone;
               }

               public List<String> getInterests() {
                   return this.interests;
               }

               public void setAge(String age) {
                   this.age = age;
               }

               public void setTelephone(String telephone) {
                   this.telephone = telephone;
               }

               public void setInterests(List<String> interests) {
                   this.interests = interests;
               }
           }
       }

[arthas@3633]$

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

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

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

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

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

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

redefine命令

在这里插入图片描述

从这张图可以明显的看出,明明源码中没有打印字符串 s 的逻辑,但是控制台还是打印了字符串,因为我们已经热替换了 JVM 内存中(方法区)加载的类。

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

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

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

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

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

tee命令

tt -t com.shockang.study.ArthasDemo convert | tee /Users/shockang/Downloads/log

此外还可以使用 monitor 命令统计方法调用成功失败情况。

在这里插入图片描述

monitor -c 30 com.shockang.study.ArthasDemo convert | tee /Users/shockang/Downloads/log1

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

问题 5:是否有一个全局视角来查看系统的运行状况?

使用 dashboard 命令可以查看当前系统的实时数据面板,
当运行在Ali-tomcat时,会显示当前tomcat的实时信息,如HTTP请求的qps, rt, 错误数, 线程池信息等等。

在这里插入图片描述

从图中可以看到线程情况,内存使用情况,系统参数等。

问题 6:有什么办法可以监控到JVM的实时运行状态?

使用 jvm 命令可以查看 JVM 的实时运行状态。

在这里插入图片描述

问题 7:怎么快速定位应用的热点,生成火焰图?

profiler 命令支持生成应用热点的火焰图。本质上是通过不断的采样,然后把收集到的采样结果生成火焰图。

默认情况下,生成的是 cpu 的火焰图,即 event 是 cpu,可以用–event 参数来指定。注意不同系统支持的 event 不同

profiler命令
默认情况下,arthas使用3658端口,则可以打开: http://localhost:3658/arthas-output/ 查看到arthas-output目录下面的profiler结果:

profiler目录

选择一项点击

在这里插入图片描述

问题 8:怎样直接从JVM内查找某个类的实例?

使用 vmtool 可以达成目的

这个功能是 Arthas 3.5.1 新增的。可以参考官方文档 vmtool — Arthas 3.5.2 文档 (aliyun.com)

$ vmtool --action getInstances --className java.lang.String --limit 10
@String[][
    @String[com/taobao/arthas/core/shell/session/Session],
    @String[com.taobao.arthas.core.shell.session.Session],
    @String[com/taobao/arthas/core/shell/session/Session],
    @String[com/taobao/arthas/core/shell/session/Session],
    @String[com/taobao/arthas/core/shell/session/Session.class],
    @String[com/taobao/arthas/core/shell/session/Session.class],
    @String[com/taobao/arthas/core/shell/session/Session.class],
    @String[com/],
    @String[java/util/concurrent/ConcurrentHashMap$ValueIterator],
    @String[java/util/concurrent/locks/LockSupport],
]

通过 --limit参数,可以限制返回值数量,避免获取超大数据时对JVM造成压力。默认值是10。

如果想精确的定位到具体的类实例,可以通过指定 classloader name 或者 classloader hash,如下所示:

vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext
vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext

获取 classloader hash 的方法请参考上面的问题 1

vmtool 还有个不错的功能,可以强制进行GC,这在某些生产环境内存紧张的情况下有奇效。

vmtool --action forceGc
评论 21 您还未登录,请先 登录 后发表或查看评论
<p style="color: #676767; font-size: 14px; background-color: #ffffff;"><strong><span style="color: #008781; font-size: 16px;">为什么要学JVM</span></strong></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">1、切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">3、同时,JVM又是各大软件公司笔试、面试的重中之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之。</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><strong><span style="color: #008781; font-size: 16px;">课程介绍</span></strong></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">本课程包含11个大章节,总计102课时,无论是笔试、面试,是日常工作,可以让您游刃有余。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;"><img src="https://img-bss.csdnimg.cn/202105152200003850.png" alt="" /></span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第3章 类的加载、解释、编译,本章节带你深入理解类加载器的分类、范围、双亲委托策略,自己手写类加载器,理解字节码解释器、即时编译器、混合模式、热点代码检测、分层编译等核心知识。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数器、虚拟机栈、本地方法栈、方法区、永久代、元空间等全部内容。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全区,四种引类型的使和区别等等。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第7章 GC算法与收集器,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收器的原理和使,垃圾回收器的组合及分代收集等。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第8章 GC日志详解,各种垃圾回收器的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第10章 阿里巴巴Arthas在线诊断工具,这是个特别小惊喜,教您怎样使当前最火热的arthas调优工具,在线诊断各种JVM问题。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">第11章 故障排除,本章会使实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><strong><span style="color: #008781; font-size: 16px;">课程资料</span></strong></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">课程附带配套项目源码2个</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">159页高清PDF理论篇课件1份</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">89页高清PDF实战篇课件1份</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">Unsafe源码PDF课件1份</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">class_stats字段说明PDF文件1份</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">jcmd Thread.print解析说明文件1份</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">JProfiler内存工具说明文件1份</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">字节码可视化解析工具1份</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">GC日志可视化工具1份</span></span></p> <p style="color: #676767; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">命令行工具cmder 1份</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><strong><span style="color: #008781; font-size: 16px;">学习方法</span></strong></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">理论篇部分推荐每天学习2课时,可以在公交地铁上手机进行学习。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">实战篇部分推荐对照视频,使配套源码,边练习遍学习。</span></span></p> <p style="color: #676767; font-size: 14px; background-color: #ffffff;"><span style="color: #676767;"><span style="font-size: 14px;">课程内容较多,不要次性学太多,而是要循序渐进,坚持学习。</span></span></p> <p><img src="https://img-bss.csdnimg.cn/202103270814406060.jpg" alt="" /></p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p> <p> </p>
©️2022 CSDN 皮肤主题:酷酷鲨 设计师:CSDN官方博客 返回首页

打赏作者

Shockang

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值