Contents

Arthas 简介

1. 简介

阿里巴巴 Arthas 是一个诊断工具,使我们能够监控、分析和排除 Java 应用程序的故障。使用 Arthas 的主要好处之一是我们不需要更改代码,甚至不需要重新启动我们想要监控的 Java 服务。

在本教程中,我们将从安装 Arthas 开始,然后通过一个简单的案例研究来演示 Arthas 的一些关键特性。 最后,由于 Arthas 是用 Java 编写的,因此它是跨平台的,可以在 Linux、macOS 和 Windows 上愉快地运行。

2. 下载和入门

首先,让我们从直接通过下载链接 或使用curl下载 Arthas 库开始:

curl -O https://alibaba.github.io/arthas/arthas-boot.jar

现在,让我们通过使用*-h*(帮助)选项运行 Arthas 来测试它是否正常工作:

java -jar arthas-boot.jar -h

如果成功,我们应该看到所有显示的命令的帮助指南:

/uploads/java_alibaba_arthas_intro/1.png

3. 案例研究

在本教程中,我们将使用一个非常简单的应用程序,该应用程序基于使用递归的斐波那契数列 的一个相当低效的实现:

public class FibonacciGenerator {
    public static void main(String[] args) {
        System.out.println("Press a key to continue");
        System.in.read();
        for (int i = 0; i < 100; i++) {
            long result = fibonacci(i);
            System.out.println(format("fib(%d): %d", i, result));
        }
    }
    public static long fibonacci(int n) {
        if (n == 0 || n == 1) {
            return 1L;
        } else {
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
    }
}

这个例子最有趣的部分是遵循斐波那契数学定义的fibonacci方法。

main方法中,我们使用具有相对较大数字的for循环,因此我们的计算机将忙于更长的计算。当然,这正是我们想要展示阿尔萨斯的目的。

4. 启动Arthas

现在让我们试试Arthas吧!我们需要做的第一件事是运行我们的小型 Fibonacci 应用程序。为此,我们可以使用我们最喜欢的 IDE 或直接在终端中运行它。它会要求按一个键才能开始。将流程附加到 Arthas 后,我们将按任意键。

现在,让我们运行 Arthas 可执行文件:

java -jar arthas-boot.jar

Arthas 会提示一个菜单来选择我们要附加到哪个进程:

[INFO] arthas-boot version: 3.1.7
[INFO] Found existing java process, please choose one and hit RETURN.
* [1]: 25500 com.blogdemo.arthas.FibonacciGenerator
...

让我们选择名称为com.blogdemo.arthas.FibonacciGenerator的那个。只需在列表中输入数字,在本例中为“1”,然后按 Enter。

Arthas 现在将附加到此进程并开始:

INFO] Try to attach process 25500
[INFO] Attach process 25500 success.
...

一旦我们启动了 Arthas,我们就会有一个提示,我们可以在其中发出不同的命令。

我们可以使用help命令来获取有关可用选项的更多信息。而且,为了方便Arthas的使用,我们还可以使用tab键来自动补全它的命令。

将 Arthas 附加到我们的进程后,现在我们可以按一个键,程序开始打印斐波那契数。

5. 仪表板

一旦 Arthas 启动,我们就可以使用仪表板。在这种情况下,我们继续输入dashboard命令。现在我们看到一个详细的屏幕,其中包含多个窗格以及有关我们的 Java 进程的大量信息:

/uploads/java_alibaba_arthas_intro/3.png

让我们更详细地看一下其中的一些:

  1. 顶部专门用于当前运行的线程
  2. 其中一个重要的列是每个线程的 CPU 消耗
  3. 第 3 节显示每个线程的 CPU 时间
  4. 另一个有趣的窗格是用于内存分析。列出了不同的内存区域及其统计信息。在右侧,我们有关于垃圾收集器 的信息
  5. 最后,在第 5 节中,我们有关于主机平台和 JVM 的信息

我们可以按q退出仪表板。

我们应该记住,即使我们退出,Arthas也会加入我们的流程。因此,为了正确地将其与我们的进程取消链接,我们需要运行stop命令

6. 分析堆栈跟踪

在仪表板中,我们看到我们的进程占用了几乎 100% 的 CPU。该进程的ID为 1,我们可以在第一列中看到。 现在我们已经退出了仪表板,我们可以通过运行thread命令来更详细地分析该过程:

thread 1

作为参数传递的数字是线程 ID。Arthas 打印出一个堆栈跟踪,不出所料,其中充斥着对fibonacci方法的调用。 如果堆栈跟踪很长且难以阅读,线程命令允许我们使用管道:

thread 1 | grep 'main('

这只会打印与 grep命令匹配的行:

[arthas@25500]$ thread 1 | grep 'main('
    at com.blogdemo.arthas.FibonacciGenerator.main(FibonacciGenerator.java:10)

7. 反编译Java类

让我们想象一个场景,我们正在分析一个我们知之甚少或一无所知的 Java 应用程序,我们突然发现堆栈中到处都是重复的类型调用:

[arthas@59816]$ thread 1
"main" Id=1 RUNNABLE
  at app//com.blogdemo.arthas.FibonacciGenerator.fibonacci(FibonacciGenerator.java:18)
  at app//com.blogdemo.arthas.FibonacciGenerator.fibonacci(FibonacciGenerator.java:18)
  ...

由于我们正在运行 Arthas,**我们可以反编译一个类来查看它的内容。**为此,我们可以使用jad 命令,将合格的类名作为参数传递:

jad com.blogdemo.arthas.FibonacciGenerator
ClassLoader:
+-jdk.internal.loader.ClassLoaders$AppClassLoader@799f7e29
  +-jdk.internal.loader.ClassLoaders$PlatformClassLoader@60f1dd34
Location:
/home/amoreno/work/blogdemo/tutorials/libraries-3/target/
/*
 * Decompiled with CFR.
 */
package com.blogdemo.arthas;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
public class FibonacciGenerator {
    public static void main(String[] arrstring) throws IOException {

输出是反编译的 Java 类和一些有用的元数据,例如类的位置。这是一个非常有用且强大的功能。

8. 搜索类和搜索方法

在搜索 JVM 中加载的类时,search class 命令会派上用场。我们可以通过键入sc并传递带有或不带有通配符的模式作为参数来使用它:

[arthas@70099]$ sc *Fibonacci*
com.blogdemo.arthas.FibonacciGenerator
Affect(row-cnt:1) cost in 5 ms.

一旦我们有了类的限定名称,我们就可以使用两个附加标志来查找更多信息:

  • -d显示类的详细信息
  • -f显示类的字段

但是,必须结合详细信息查询类的字段:

[arthas@70099]$ sc -df com.blogdemo.arthas.FibonacciGenerator
  class-info        com.blogdemo.arthas.FibonacciGenerator
  ...

同样,我们可以使用命令sm(搜索方法)来查找类中加载的方法。在这种情况下,对于我们的类com.blogdemo.arthas.FibonacciGenerator,我们可以运行:

[arthas@70099]$ sm com.blogdemo.arthas.FibonacciGenerator
com.blogdemo.arthas.FibonacciGenerator <init>()V
com.blogdemo.arthas.FibonacciGenerator main([Ljava/lang/String;)V
com.blogdemo.arthas.FibonacciGenerator fibonacci(I)J
Affect(row-cnt:3) cost in 4 ms.

我们也可以使用标志-d*来检索方法的详细信息*。最后,我们可以将方法的名称作为可选参数传递,以缩小返回方法的数量:

sm -d com.blogdemo.arthas.FibonacciGenerator fibonacci
 declaring-class  com.blogdemo.arthas.FibonacciGenerator
 method-name      fibonacci
 modifier         public,static
 annotation
 parameters       int
 return           long
 exceptions
 classLoaderHash  799f7e29

9. 监控方法调用

我们可以用 Arthas 做的另一件很酷的事情是监控一个方法。这在调试我们的应用程序中的性能问题时非常方便。为此,我们可以使用*monitor 命令。 monitor命令需要一个标志-c <seconds>*和两个参数——限定类名和方法名。

对于我们的案例研究,现在让我们调用monitor

monitor -c 10 com.blogdemo.arthas.FibonacciGenerator fibonacci

正如预期的那样,Arthas 将每 10 秒打印一次关于fibonacci方法的指标:

Affect(class-cnt:1 , method-cnt:1) cost in 47 ms.
##  timestamp            class                                          method     total   success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------------------------------------
 2020-03-07 11:43:26  com.blogdemo.arthas.FibonacciGenerator  fibonacci  528957  528957   0     0.07        0.00%
...

我们还有那些最终失败的调用的指标——这些对于调试很有用。

10. 监控方法参数

如果我们需要调试方法的参数,我们可以使用watch命令。但是,语法有点复杂:

watch com.blogdemo.arthas.FibonacciGenerator fibonacci '{params[0], returnObj}' 'params[0]>10' -n 10

让我们详细看看每个参数:

  • 第一个参数是类名
  • 第二个是方法名
  • 第三个参数是一个OGNL 表达式 ,定义了我们想要观察的内容——在这种情况下,它是第一个(也是唯一一个)方法参数,以及返回值
  • 第四个也是最后一个可选参数是一个布尔表达式,用于过滤我们要监视的调用

对于此示例,我们只想在参数大于 10 时对其进行监控。最后,我们添加一个标志以将结果数限制为 10:

watch com.blogdemo.arthas.FibonacciGenerator fibonacci '{params[0], returnObj}' 'params[0]>10' -n 10
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 19 ms.
ts=2020-02-17 21:48:08; [cost=30.165211ms] result=@ArrayList[
    @Integer[11],
    @Long[144],
]
ts=2020-02-17 21:48:08; [cost=50.405506ms] result=@ArrayList[
    @Integer[12],
    @Long[233],
]
...

在这里,我们可以看到调用示例及其 CPU 时间和输入/返回值。

11. 探查器

*对应用程序性能感兴趣的人可以通过profiler *命令获得非常直观的功能。**分析器将评估我们的进程正在使用的 CPU 的性能。

让我们通过启动profiler start来运行 profiler 。这是一个非阻塞任务,这意味着我们可以在分析器工作时继续使用 Arthas。

在任何时候,我们都可以通过运行profiler getSamples来询问 profiler 有多少样本。

现在让我们使用profiler stop 来停止 profiler。至此,FlameGraph 图像被保存。在这个精确的例子中,我们有一个fibonacci线主导图形的图表:

/uploads/java_alibaba_arthas_intro/5.png

请注意,当我们想要检测 CPU 时间花费在哪里时,此图表特别有用。