本文从Arthas的基础运用深入到实战演练,希望能协助广阔Java Boy掌握这一监控剖析利器。

一、前语

1.1 简介

先引用一下官网的原话给咱们介绍一下什么是Arthas(PS:读作阿尔萨斯)

Arthas 是一款线上监控确诊产品,经过全局视角实时检查运用 load、内存、gc、线程的状况信息,并能在不修正运用代码的状况下,对事务问题进行确诊,包含检查办法调用的出入参、反常,监测办法履行耗时,类加载信息等,大大提升线上问题排查功率。

1.2背景

一般,本地开发环境无法访问出产环境。假如在出产环境中遇到问题,则无法运用 IDE 长途调试。更糟糕的是,在出产环境中调试是不可接受的,因为它会暂停一切线程,导致服务暂停。

针对现状进行剖析,咱们能够发现,当时的问题排查存在以下几个痛点。

  • 关于一些反常问题来说,比方常见的NullPointerException、等,咱们能够经过反常仓库来查找相关的问题,可是,关于一些并没有呈现反常的问题,往往不是那么好排查

  • 开发人员能够测验在测验环境或许预发环境中复现出产环境中的问题。可是,某些问题无法在不同的环境中轻松复现,甚至在从头发动后就消失了

  • 一种常见的解决办法是:在代码中增加一些日志,经过日志来判别事务的运转状况,数据的流转状况等,然后判别该问题的原因,可是该办法必须经历

    1. 切换hotfix分支
    2. 增加相应的日志代码
    3. Merge代码到Master
    4. 布置线上环境

    这种办法功率低下,而且假如日志记录的不是很完善的话,又需求打上新的日志来判别问题,而且,许多的日志也会给体系带来没必要的担负

  • 另外存在一种更少见的状况,那就是该问题是因为JVM内存/GC引起的,比方GC后本地缓存失效。关于此种问题,一旦JVM从头发动,它或许在短时刻内无法复现

Arthas 旨在解决以上这些问题。开发人员能够在线解决出产问题。无需 JVM 重启,无需代码更改。 Arthas 作为调查者永远不会暂停正在运转的线程。

1.3Arthas能为你做什么

Arthas 是 Alibaba 开源的 Java 确诊东西,深受开发者喜爱。

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

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

Arthas 支撑 JDK 6+,支撑 Linux/Mac/Windows,采用指令行交互形式,一起供给丰厚的 Tab 主动补全功用,进一步方便进行问题的定位和确诊。

二、运用

在教咱们怎么运用前,更应该教咱们怎么学习Arthas的运用。

因为Arthas运用场景往往是线上问题的监控与剖析,需求咱们快准狠的解决问题。Arthas的指令很多,可是因为有Arthas-Idea插件,以及官方文档和搜索引擎,在这儿,我并不引荐咱们把指令死记硬背下来,咱们应该搞清楚Arthas它能够帮我做什么工作,如1.3提到的那些功用,咱们应该对它具有的功用十分了解,而且,能够知道该功用对应的指令是啥(这儿记不住的话也行,能够查资料,可是一定要了解它有哪些功用,不然在真正的运用中,你将无法想到能够运用Arthash)。

Arthas-Idea插件安装

实际运用中,假如自己手动输指令,超长的类名绝对是噩梦,就算是仿制粘贴也是很费事的,比方用的许多的watch,trace这种。不过还好,现在有插件能帮咱们脱节这个痛苦。

一起来学学Arthas这个JVM监控分析神器吧!

安装完插件之后,右键点击类或许办法,点击Arthas Command,咱们能够发现右边呈现了许多选项,这些选项对应着Arthas里面的功用,咱们只需求单击其间一个,就能够仿制相关的指令到剪贴板,然后去Arthas控制台运用即可,假如想修正指令的参数(常见的比方-n:监控最大履行次数,-x:回来嵌套的最大深度),咱们能够自己手动修正然后再履行。

一起来学学Arthas这个JVM监控分析神器吧!

这真是一个伟大的插件!

快速入门

下面的内容摘自官方文档:Arthas文档

1. 发动 math-game

curl -O https://arthas.aliyun.com/math-game.jar
java -jar math-game.jar

math-game是一个简单的程序,每隔一秒生成一个随机数,再履行质因数分化,并打印出分化成果。

math-game源代码:检查在新窗口打开

2. 发动 arthas

在指令行下面履行(运用和方针进程共同的用户发动,否则或许 attach 失败):

curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
  • 履行该程序的用户需求和方针进程具有相同的权限。比方以admin用户来履行:sudo su admin && java -jar arthas-boot.jarsudo -u admin -EH java -jar arthas-boot.jar
  • 假如 attach 不上方针进程,能够检查~/logs/arthas/ 目录下的日志。
  • 假如下载速度比较慢,能够运用 aliyun 的镜像:java -jar arthas-boot.jar --repo-mirror aliyun --use-http
  • java -jar arthas-boot.jar -h 打印更多参数信息。

挑选运用 java 进程:

$ $ java -jar arthas-boot.jar
* [1]: 35542
  [2]: 71560 math-game.jar

math-game进程是第 2 个,则输入 2,再输入回车/enter。Arthas 会 attach 到方针进程上,并输出日志:

[INFO] Try to attach process 71560
[INFO] Attach process 71560 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.  ,---.
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '  .-'
|  .-.  || '--'.'  |  |  |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \   |  |  |  |  |  ||  | |  |.-'   |
`--' `--'`--' '--'  `--'  `--'  `--'`--' `--'`-----'
​
​
wiki: https://arthas.aliyun.com/doc
version: 3.0.5.20181127201536
pid: 71560
time: 2018-11-28 19:16:24
​
$

3. 检查 dashboard

输入dashboard,按回车/enter,会展现当时进程的信息,按ctrl+c能够中断履行。

$ dashboard
ID   NAME          GROUP      PRIORI STATE  %CPU   TIME  INTERRU DAEMON
17   pool-2-thread-1     system     5    WAITIN 67   0:0  false  false
27   Timer-for-arthas-dashb system     10   RUNNAB 32   0:0  false  true
11   AsyncAppender-Worker-a system     9    WAITIN 0    0:0  false  true
9    Attach Listener     system     9    RUNNAB 0    0:0  false  true
3    Finalizer        system     8    WAITIN 0    0:0  false  true
2    Reference Handler    system     10   WAITIN 0    0:0  false  true
4    Signal Dispatcher    system     9    RUNNAB 0    0:0  false  true
26   as-command-execute-dae system     10   TIMED_ 0    0:0  false  true
13   job-timeout       system     9    TIMED_ 0    0:0  false  true
1    main          main      5    TIMED_ 0    0:0  false  false
14   nioEventLoopGroup-2-1  system     10   RUNNAB 0    0:0  false  false
18   nioEventLoopGroup-2-2  system     10   RUNNAB 0    0:0  false  false
23   nioEventLoopGroup-2-3  system     10   RUNNAB 0    0:0  false  false
15   nioEventLoopGroup-3-1  system     10   RUNNAB 0    0:0  false  false
Memory       used  total max   usage GC
heap        32M   155M  1820M 1.77% gc.ps_scavenge.count 4
ps_eden_space    14M   65M  672M  2.21% gc.ps_scavenge.time(m 166
ps_survivor_space  4M   5M   5M      s)
ps_old_gen     12M   85M  1365M 0.91% gc.ps_marksweep.count 0
nonheap       20M   23M  -1      gc.ps_marksweep.time( 0
code_cache     3M   5M   240M  1.32% ms)
Runtime
os.name         Mac OS X
os.version       10.13.4
java.version      1.8.0_162
java.home        /Library/Java/JavaVir
            tualMachines/jdk1.8.0
            _162.jdk/Contents/Hom
            e/jre

4. 经过 thread 指令来获取到math-game进程的 Main Class

thread 1会打印线程 ID 1 的栈,一般是 main 函数的线程。

参数称号 参数阐明
id 线程 id
[n:] 指定最忙的前 N 个线程并打印仓库
[b] 找出当时堵塞其他线程的线程
[i <value>] 指定 cpu 运用率计算的采样间隔,单位为毫秒,默许值为 200
[–all] 显现一切匹配的线程
$ thread 1 | grep 'main('
   at demo.MathGame.main(MathGame.java:17)

5. 经过 jad 来反编译 Main Class

$ jad demo.MathGame
​
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@3d4eac69
 +-sun.misc.Launcher$ExtClassLoader@66350f69
​
Location:
/tmp/math-game.jar
​
/*
 * Decompiled with CFR 0_132.
 */
package demo;
​
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
​
public class MathGame {
  private static Random random = new Random();
  private int illegalArgumentCount = 0;
​
  public static void main(String[] args) throws InterruptedException {
    MathGame game = new MathGame();
    do {
      game.run();
      TimeUnit.SECONDS.sleep(1L);
     } while (true);
   }
​
  public void run() throws InterruptedException {
    try {
      int number = random.nextInt();
      List<Integer> primeFactors = this.primeFactors(number);
      MathGame.print(number, primeFactors);
     }
    catch (Exception e) {
      System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) + e.getMessage());
     }
   }
​
  public static void print(int number, List<Integer> primeFactors) {
    StringBuffer sb = new StringBuffer("" + number + "=");
    Iterator<Integer> iterator = primeFactors.iterator();
    while (iterator.hasNext()) {
      int factor = iterator.next();
      sb.append(factor).append('*');
     }
    if (sb.charAt(sb.length() - 1) == '*') {
      sb.deleteCharAt(sb.length() - 1);
     }
    System.out.println(sb);
   }
​
  public List<Integer> primeFactors(int number) {
    if (number < 2) {
      ++this.illegalArgumentCount;
      throw new IllegalArgumentException("number is: " + number + ", need >= 2");
     }
    ArrayList<Integer> result = new ArrayList<Integer>();
    int i = 2;
    while (i <= number) {
      if (number % i == 0) {
        result.add(i);
        number /= i;
        i = 2;
        continue;
       }
      ++i;
     }
    return result;
   }
}
​
Affect(row-cnt:1) cost in 970 ms.

6. watch

经过watch指令来检查demo.MathGame#primeFactors函数的回来值:

$ watch demo.MathGame primeFactors returnObj
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 107 ms.
ts=2018-11-28 19:22:30; [cost=1.715367ms] result=null
ts=2018-11-28 19:22:31; [cost=0.185203ms] result=null
ts=2018-11-28 19:22:32; [cost=19.012416ms] result=@ArrayList[
   @Integer[5],
   @Integer[47],
   @Integer[2675531],
]
ts=2018-11-28 19:22:33; [cost=0.311395ms] result=@ArrayList[
   @Integer[2],
   @Integer[5],
   @Integer[317],
   @Integer[503],
   @Integer[887],
]
ts=2018-11-28 19:22:34; [cost=10.136007ms] result=@ArrayList[
   @Integer[2],
   @Integer[2],
   @Integer[3],
   @Integer[3],
   @Integer[31],
   @Integer[717593],
]
ts=2018-11-28 19:22:35; [cost=29.969732ms] result=@ArrayList[
   @Integer[5],
   @Integer[29],
   @Integer[7651739],
]

更多的功用能够检查进阶运用。

7. 退出 arthas

假如只是退出当时的衔接,能够用quit或许exit指令。Attach 到方针进程上的 arthas 还会继续运转,端口会保持敞开,下次衔接时能够直接衔接上。

假如想彻底退出 arthas,能够履行stop指令。

TIPS:因为Arthas指令很多,这儿就不在具体的介绍,咱们能够自行去官方文档和官方教程中学习。

除此之外,再给咱们引荐官方的Arthas教程。

Arthas基础教程

Arthas进阶教程

三、实践

下面针对我个人关于Arthas常见的运用场景,给咱们带来了一些指令的实际场景。

3.1、占用CPU最高的n个线程是哪些?

有时分咱们会发现机器cpu飙升,这时咱们最想要的就是看,究竟是那些线程在做耗CPU的工作,把这些线程的仓库打印出来,然后能够找到履行的代码,这时分thread -n就是一个不错挑选。

如下运用thread -n 3 显现进程中耗CPU最高的三个线程,按照CPU耗费量递减排序显现。

$ thread -n 3
"C1 CompilerThread0" [Internal] cpuUsage=1.63% deltaTime=3ms time=1170ms
"arthas-command-execute" Id=23 cpuUsage=0.11% deltaTime=0ms time=401ms RUNNABLE
    at java.management@11.0.7/sun.management.ThreadImpl.dumpThreads0(Native Method)
    at java.management@11.0.7/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:466)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:199)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
    at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
    at java.base@11.0.7/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base@11.0.7/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base@11.0.7/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
    at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base@11.0.7/java.lang.Thread.run(Thread.java:834)
"VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=0ms time=584ms

经过剖析线程仓库,咱们能够解决CPU耗费过高的问题。

当然,除此之外,咱们也能够运用Top指令找出占有CPU最多的N个线程,然后再经过jstack指令来进行仓库剖析。这儿给想学习这种办法的读者引荐另一位作者的文章。 运用top+jstack排查CPU运用率100%

读者们能够依据自己的喜好来挑选相应的东西。

3.2、哪个线程堵塞了其他线程?

有时分咱们发现运用卡住了, 这或许是因为发生了死锁或许其他锁等待问题。 为了排查这类问题, arthas供给了thread -b, 一键找出那个元凶巨恶。

如下运用thread -b 显现进程中称号为biz2的线程在获取biz1线程持有的对象时被堵塞。

[arthas@36585]$ thread -b
"biz2" Id=12 BLOCKED on java.lang.Object@ba7da9c owned by "biz1" Id=11
    at org.JDk8Stream.App$2.run(App.java:81)
    -  blocked on java.lang.Object@ba7da9c
    -  locked java.lang.Object@549c3b6a <---- but blocks 1 other threads!
    at java.lang.Thread.run(Thread.java:748)

经过剖析线程仓库,咱们能够知道是at org.JDk8Stream.App$2.run(App.java:81)获取的锁对象

注意, 目前只支撑找出 synchronized 关键字堵塞住的线程, 假如是java.util.concurrent.Lock, 目前还不支撑。

假如是Lock锁,咱们能够运用jstack指令来排查死锁。相同,jstack指令也能用来排查synchronized锁,

假如发生了死锁,jstack指令就会展现以下信息:咱们也能够定位到问题。

Found one Java-level deadlock:
=============================
"mythread2":
  waiting for ownable synchronizer 0x000000076aea4848, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "mythread1"
"mythread1":
  waiting for ownable synchronizer 0x000000076aea4878, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "mythread2"
Java stack information for the threads listed above:
===================================================
"mythread2":
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076aea4848> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
	at com.hua.jvm.DeathLockDemo.lambda$deathLock$1(DeathLockDemo.java:39)
	at com.hua.jvm.DeathLockDemo$$Lambda$2/240650537.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)
"mythread1":
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076aea4878> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
	at com.hua.jvm.DeathLockDemo.lambda$deathLock$0(DeathLockDemo.java:29)
	at com.hua.jvm.DeathLockDemo$$Lambda$1/1809787067.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.

3.3、我修正的代码在线上收效了?

这是我用的最多的一个功用,测验以及调试bug的时分,总是会不记得测验环境布置的代码是不是最新的。又或许有时分咱们修正了代码,发布到了线上,可是成果或许不契合预期或许没法验证是否契合预期(比方非热门代码),那么咱们或许会想,我的代码究竟有没有被JVM加载收效了?

Artahs供给了,jad 指令 能够协助咱们讲 JVM 中实际运转的 class 的 byte code 反编译成 java 代码,便于你理解事务逻辑

$ jad --source-only demo.MathGame
/*
 * Decompiled with CFR 0_132.
 */
package demo;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class MathGame {
    private static Random random = new Random();
    public int illegalArgumentCount = 0;
...

3.4、调查一个办法的入参和回来值

当线上呈现事端时分,有时分咱们想看相关事务某个办法履行的入参和出参数据,然后确认办法逻辑履行的正确性,或许入参的合法性,而刚好没打日志,这时分怎样办呢?

Arthas供给了watch指令,用于动态调查办法履行数据,假如咱们的入参和出参比较复杂,那咱们能够经过-x参数指定输出成果的属性遍历深度,如下所示

$ watch demo.MathGame primeFactors -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 32 ms, listenerId: 5
method=demo.MathGame.primeFactors location=AtExceptionExit
ts=2021-08-31 15:22:57; [cost=0.220625ms] result=@ArrayList[
    @Object[][
        @Integer[-179173],
    ],
    @MathGame[
        random=@Random[java.util.Random@31cefde0],
        illegalArgumentCount=@Integer[44],
    ],
    null,
]
method=demo.MathGame.primeFactors location=AtExit
ts=2021-08-31 15:22:58; [cost=1.020982ms] result=@ArrayList[
    @Object[][
        @Integer[1],
    ],
    @MathGame[
        random=@Random[java.util.Random@31cefde0],
        illegalArgumentCount=@Integer[44],
    ],
    @ArrayList[
        @Integer[2],
        @Integer[2],
        @Integer[26947],
    ],
]
  • 咱们能够发现Result数组里有三个元素,这是调查表达式的值,默许值是{params, target, returnObj},该表达式能够经过参数配置

    • params:入参
    • target:this对象
    • returnObj:出参
  • 上面的成果里,阐明函数被履行了两次,第一次成果是location=AtExceptionExit,阐明函数抛出反常了,因而returnObj是 null

  • 在第2次成果里是location=AtExit,阐明函数正常回来,因而能够看到returnObj成果是一个 ArrayList

今后遇到线上问题时,不要再傻呵呵的打日志然后从头布置了,试试watch指令把。

3.5、某个办法调用链路是什么和履行耗时是多少?

有时分咱们发现某个接口的rt很长,而且接口内有很多逻辑,可是咱们并没有给每个逻辑都加时刻监控埋点,这时分咱们怎么去查找到耗时比较多的逻辑那?

Artahs则供给了trace指令来协助咱们解决这个问题,其能够洞察办法内部调用途径,并输出办法途径上的每个节点上耗时,例如下面

$ trace demo.MathGame run
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 28 ms.
`---ts=2019-12-04 00:45:08;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[0.617465ms] demo.MathGame:run()
        `---[0.078946ms] demo.MathGame:primeFactors() #24 [throws Exception]
`---ts=2019-12-04 00:45:09;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[1.276874ms] demo.MathGame:run()
        `---[0.03752ms] demo.MathGame:primeFactors() #24 [throws Exception]

今后领导叫你优化某个接口,试试trace指令把,比打一堆没意义的监控日志方便多了。

3.6、办法履行状况监控

并不是一切办法都会有监控的,大多数状况HTTP接口和RPC接口都有履行状况监控,可是程序内部的办法往往是没有监控,比方本地缓存,假如咱们这时分想看一下本地缓存的调用状况,应该怎样办呢?

当咱们想看一下线上环境某个办法的履行状况,咱们就能够运用monitor指令来检查

$ monitor -c 5 demo.MathGame primeFactors
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 94 ms.
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:38  demo.MathGame  primeFactors  5      1        4     1.15        80.00%
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:43  demo.MathGame  primeFactors  5      3        2     42.29       40.00%
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:48  demo.MathGame  primeFactors  5      3        2     67.92       40.00%
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:53  demo.MathGame  primeFactors  5      2        3     0.25        60.00%
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:06:58  demo.MathGame  primeFactors  1      1        0     0.45        0.00%
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2018-12-03 19:07:03  demo.MathGame  primeFactors  2      2        0     3182.72     0.00%

咱们能够看到该时刻段内总的调用次数、成功的次数、失败的次数以及平均履行时刻和失败率等指标,关于某个接口的性能和吞吐量来说具有不错的参考价值。

3.7、生成火焰图

火焰图是很常用的一个问题剖析东西,能够高度可视化的反映出体系的履行细节以及瓶颈。Arthas供给的profiler指令能够快速生成火焰图。

  1. 履行 profiler start,开始采样。默许进行CPU采样,可经过参数**–event**指定采样事件,如cpu、alloc、lock、wall

    $ profiler start
    Started [cpu] profiling
    
  2. 继续采样ing…,可经过profiler status检查当时采样状况

    $ profiler status
    [cpu] profiling is running for 4 seconds
    
  3. 获取已采样的Sample数量

    $ profiler getSamples
    23
    
  4. 履行profiler stop,停止采样。默许状况下,成果文件是html格局,也能够用--format参数指定:

    $ profiler stop --format html
    profiler output file: /tmp/test/arthas-output/20211207-111550.html
    OK
    

    或许在--file参数里用文件名指名格局。比方--file /tmp/result.html

四、总结

Java的监控、性能剖析东西很多,无论是平常运用,还是面试时分跟面试官吹逼,不要只了解那几个JDK基础指令了,高级的东西咱们也学习来用起来。