Arthas备忘

如果应用上线后运行效果不符合预期,而又缺少必要的日志无法观测输入参数,同时线下也无法复现。大多数人应该也没有多隆大神看代码找 BUG 的神技,这个时候你就需要一款可以在线探测、热更 JVM 的神器:Arthas。Arthas 不仅可以解决上面的问题,在定位 ClassNotFoundException、程序耗时分析、线程死锁等问题方面都可以说是利器。在 Arthas 官网其实已经有很好的帮助文档了,这里更多的是做常用命令的记录,在需要的时候可以快速复制、修改、运行。

前期准备

首先需要下载 Arthas 工具,在官网链接中下载最新版即可,下载完成后进行解压。

演示用的是 Spring MVC Web 应用,用的几个类如下:

package com.roc.web.controller;

import lombok.Getter;
import lombok.Setter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * 用于 arthas 的演示
 *
 * @author 鱼蛮 on 2021/12/15
 **/
@RestController
public class ArthasController {

    /**
     * 名称
     */
    @Getter
    @Setter
    private String name;

    /**
     * 锁对象
     */
    private final Object lock = new Object();

    /**
     * 用于 arthas 的演示
     *
     * @param name 输入名称
     * @return {@link String}
     */
    @GetMapping(value = "/arthas")
    public String arthas(@RequestParam("name") String name) {
        setName(name);
        return getName() + "_成功";
    }

    /**
     * 用于 arthas block 的演示
     *
     * @return {@link String}
     */
    @GetMapping(value = "/block")
    public String block() throws InterruptedException {
        new Thread(() -> {
            synchronized (lock) {
                // do nothing
            }
        }).start();
        synchronized (lock) {
            TimeUnit.SECONDS.sleep(30);
        }
        return "成功";
    }
}
/**
 * arthas 测试
 *
 * @author 鱼蛮 on 2021/12/15
 **/
public class ArthasTest {

    /**
     * 装饰数据
     *
     * @param input 输入
     * @return {@link String}
     */
    public String decorate(String input) {
        return input + "_arthas";
    }
}

首先启动我们的 Web 应用,再到 Arthas 解压出的文件夹中执行命令“java -jar arthas-boot.jar”,来启动 Arthas。

启动arthas

可以看到 Arthas 会自动检查系统中启动的 Java 应用,我们选择要进行测试的应用后回车,就可以进入 Arthas 的控制台了。

系统属性命令

dashboard 命令类似于系统的 top 命令,可以动态的列出 JVM 中的线程运行情况,及内存的分配情况、JVM 基本属性等,在观察系统整体运行情况时候是比较有用的。

jvm、sysprop、sysenv、vmoption 则可以用来查看 JVM 启动时候的系统参数、配置参数、诊断参数等。

thread

thread 命令与 JDK 自带的 jstack 命令类似,可以用来观察 JVM 中的线程运行情况,不过 thread 命令提供了更多高级功能。

## -n 3 表示找出最繁忙的 3 个
## -i 5000 代表使用率的采样间隔为 5000ms
thread -n 3 -i 5000

可以使用上面的命令找出 JVM 中最繁忙的 3 个线程。

## -b 表示找出当前阻塞其他线程的线程
thread -b

我们在浏览器中访问:http://127.0.0.1:8070/block,用于执行程序中预设的线程阻塞代码,然后再执行“thread -b”命令,可以看到如下的线程阻塞情况。

ognl

ognl 命令可以通过 ognl 表达式来帮助我们执行一些线上代码。

## -c 执行表达式的 ClassLoader 的 hashcode,默认值是 SystemClassLoader
ognl -c 18b4aac2 'new ArthasTest().decorate("zhangsan")'

sc

sc 命令用于查看 JVM 已加载的类信息。

## -d 输出当前类的详细信息
## -f 输出当前类的成员变量信息
sc -d -f com.roc.web.controller.ArthasController

sm

sm 命令用于查看 JVM 已加载类的方法信息。

## -d 展示每个方法的详细信息
sm -d ArthasTest

dump

dump 命令用于将已加载类的字节码输出到指定的目录。

 ## -d 输出类文件的目标目录
 dump -d  /Users/yuman/logs/arthas/ com.roc.web.controller.ArthasController

jad

jad 命令用于反编译指定类,这个功能可以用来帮助排查部署的代码是否有非预期代码。

## --source-only 只打印源代码,默认情况下会带有 ClassLoader 信息
jad --source-only com.roc.web.controller.ArthasController
jad com.roc.web.controller.ArthasController arthas

通过指定方法名,也可以只反编译指定的方法。

classloader

classloader 命令可以用于查看 ClassLoader 的继承树、类加载信息及进行类加载等。在排查 ClassNotFoundException 时候可以提供有效的帮助。

## -t 打印所有ClassLoader的继承树
classloader -t

## -l 按类加载实例进行统计
classloader -l

mc

mc 命令用于编译 .java 文件生成 .class 文件。

## -c 执行表达式的 ClassLoader 的 hashcode
## -d 指定输出目录
mc -c 18b4aac2 -d /Users/yuman/logs/arthas/ /Users/yuman/logs/arthas/ArthasTest.java

retransform

retransform 命令用于加载外部的 .class 文 件,替换 JVM 已加载的类。在线上缺少关键日志而又不想重启服务的时候,可以通过这个命令替换类,达到应用热更的目的。不过这种功能还是要慎用,万一搞错了很容易造成线上故障。

下面是 Arthas 官网给出的综合使用示例:

## 反编译指定类
jad --source-only com.roc.web.controller.ArthasController > /Users/yuman/logs/arthas/ArthasController.java

## 修改反编译后的类

## 编译修改后的类
mc -d /Users/yuman/logs/arthas/ /Users/yuman/logs/arthas/ArthasController.java

## 加载指定的class 替换已有的类
retransform /Users/yuman/logs/arthas/com/roc/web/controller/ArthasController.class

我们修改 arthas() 方法中 return 这行代码,增加 “ + "_retransform" ”。

@GetMapping(value={"/arthas"})
public String arthas(@RequestParam(value="name") String name) {
/*39*/         this.setName(name);
        return this.getName() + "_成功" + "_retransform";
}

当我们执行完 retransform 命令之后,再访问这个接口,可以看到变更已经生效了。

monitor

monitor 命令用于执行方法的监控,可以用来监控方法的调用次数、成功、失败次数等。

##  -c 统计周期默认120s
monitor -c 10 com.roc.web.controller.ArthasController arthas

我们连续调用 5 次 http://127.0.0.1:8070/arthas?name=zhangsan,可以看到如下的统计结果。

 ## monitor 支持条件表达式
 monitor -c 10 com.roc.web.controller.ArthasController arthas "params[0] == 'lisi'"

monitor 使用时候支持条件表达式,比如我们使用上面的命令,只有当请求参数“name=lisi”的请求才会被监控到,而其他输入参数的调用则不会被监控到。

watch

watch 命令能方便的观察到指定函数的调用情况。能观察到:返回值、抛出异常、入参。同样,watch 命令也支持条件表达式。

## -x 表示需要遍历返回值的属性深度
## {params, target, returnObj} 是观测的返回信息
watch com.roc.web.controller.ArthasController arthas "{params, target, returnObj}" -x 2

trace

trace 命令用于观测方法内部调用路径,并输出方法路径上的每个节点的耗时。在没有其他监控能力的时候,这个功能在分析接口调用的耗时情况时候非常有用。

## -n 捕获的次数 
trace com.roc.web.controller.ArthasController arthas -n 5 '#cost < 10'

stack

stack 命令用于输出方法被调用的路径。

stack com.roc.web.controller.ArthasController arthas

tt

tt 命令可以说是 watch 命令的增强版,可以对某个方法监控多次,并暂存在内存中,通过索引可以获取每次调用的现场环境。

## -t 记录每次调用个情况 
## -n 表示需要记录的次数
tt -t -n 10 com.roc.web.controller.ArthasController arthas

## -l 检索刚才的记录信息
tt -l 
检索刚才记录的信息

## 查看某个调用过程的细节,-i 查看的索引号
tt -i 1003

你可能感兴趣的:(Arthas备忘)