前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Arthas,一个Java诊断工具,你值得拥有

Arthas,一个Java诊断工具,你值得拥有

作者头像
孟君
发布2020-05-13 17:41:07
1.1K0
发布2020-05-13 17:41:07
举报

Arthas是什么?

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

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

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

Arthas支持JDK 6+,支持Linux/Mac/Winodws,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

其运行的原理如下图所示:

Arthas是我比较喜欢的一个Java诊断工具,下面一起来体验一下吧。

一. 快速安装

可以通过如下链接下载arthas-boot.jar

代码语言:javascript
复制
https://alibaba.github.io/arthas/arthas-boot.jar

然后用java -jar的方式启动:

代码语言:javascript
复制
java -jar arthas-boot.jar

可以通过java -jar arthas-boot.jar -h查看帮助信息:

代码语言:javascript
复制
E:\develop>java -jar arthas-boot.jar -h
[INFO] arthas-boot version: 3.1.7
Usage: arthas-boot [--telnet-port <value>] [--target-ip <value>] [--repo-mirror
       <value>] [--http-port <value>] [--versions] [--arthas-home <value>]
       [--attach-only] [--use-http] [-h] [--session-timeout <value>]
       [--use-version <value>] [-v] [-f <value>] [--tunnel-server <value>]
       [--width <value>] [--agent-id <value>] [--stat-url <value>] [-c <value>]
       [--height <value>] [pid]

Bootstrap Arthas

EXAMPLES:
  java -jar arthas-boot.jar <pid>
  java -jar arthas-boot.jar --target-ip 0.0.0.0
  java -jar arthas-boot.jar --telnet-port 9999 --http-port -1
  java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'
  java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'
--agent-id bvDOe8XbTM2pQWjF4cfw
  java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'
  java -jar arthas-boot.jar -c 'sysprop; thread' <pid>
  java -jar arthas-boot.jar -f batch.as <pid>
  java -jar arthas-boot.jar --use-version 3.1.7
  java -jar arthas-boot.jar --versions
  java -jar arthas-boot.jar --session-timeout 3600
  java -jar arthas-boot.jar --attach-only
  java -jar arthas-boot.jar --repo-mirror aliyun --use-http
WIKI:
  https://alibaba.github.io/arthas

Options and Arguments:
    --telnet-port <value>       The target jvm listen telnet port, default 3658
    --target-ip <value>         The target jvm listen ip, default 127.0.0.1
    --repo-mirror <value>       Use special maven repository mirror, value is
                                center/aliyun or http repo url.
    --http-port <value>         The target jvm listen http port, default 8563
    --versions                  List local and remote arthas versions
    --arthas-home <value>       The arthas home
    --attach-only               Attach target process only, do not connect
    --use-http                  Enforce use http to download, default use https
 -h,--help                      Print usage
    --session-timeout <value>   The session timeout seconds, default 1800
                                (30min)
    --use-version <value>       Use special version arthas
 -v,--verbose                   Verbose, print debug info.
 -f,--batch-file <value>        The batch file to execute
    --tunnel-server <value>     The tunnel server url
    --width <value>             arthas-client terminal width
    --agent-id <value>          The agent id register to tunnel server
    --stat-url <value>          The report stat url
 -c,--command <value>           Command to execute, multiple commands separated
                                by ;
    --height <value>            arthas-client terminal height
 <pid>                          Target pid


E:\develop>

接下来,先体验一下Arthas的最基本的操作。

二. 快速入门

启动一个24点小程序:

代码语言:javascript
复制

import java.util.*;

public class Game24Player {

    final String[] patterns = {"nnonnoo", "nnonono", "nnnoono", "nnnonoo",
        "nnnnooo"};
    final String ops = "+-*/^";

    String solution;
    List<Integer> digits;

    public static void main(String[] args) {
        new Game24Player().play();
    }

    void play() {
        digits = getSolvableDigits();

        Scanner in = new Scanner(System.in);
        while (true) {
            //System.out.print("Make 24 using these digits: ");
          System.out.print("使用如下数字计算24点: ");
            System.out.println(digits);
            System.out.println("(输入 'q' 退出, 输入's'获取一种解决方法 )");
            System.out.print("> ");

            String line = in.nextLine();
            if (line.equalsIgnoreCase("q")) {
                System.out.println("\n谢谢");
                return;
            }

            if (line.equalsIgnoreCase("s")) {
                System.out.println(solution);
                digits = getSolvableDigits();
                continue;
            }

            char[] entry = line.replaceAll("[^*+-/)(\\d]", "").toCharArray();

            try {
                validate(entry);

                if (evaluate(infixToPostfix(entry))) {
                    System.out.println("\n正确! 再来一次? ");
                    digits = getSolvableDigits();
                } else {
                    System.out.println("\n不正确.");
                }

            } catch (Exception e) {
                System.out.printf("%n%s Try again.%n", e.getMessage());
            }
        }
    }

    void validate(char[] input) throws Exception {
        int total1 = 0, parens = 0, opsCount = 0;

        for (char c : input) {
            if (Character.isDigit(c))
                total1 += 1 << (c - '0') * 4;
            else if (c == '(')
                parens++;
            else if (c == ')')
                parens--;
            else if (ops.indexOf(c) != -1)
                opsCount++;
            if (parens < 0)
                throw new Exception("Parentheses mismatch.");
        }

        if (parens != 0)
            throw new Exception("Parentheses mismatch.");

        if (opsCount != 3)
            throw new Exception("Wrong number of operators.");

        int total2 = 0;
        for (int d : digits)
            total2 += 1 << d * 4;

        if (total1 != total2)
            throw new Exception("Not the same digits.");
    }

    boolean evaluate(char[] line) throws Exception {
        Stack<Float> s = new Stack<>();
        try {
            for (char c : line) {
                if ('0' <= c && c <= '9')
                    s.push((float) c - '0');
                else
                    s.push(applyOperator(s.pop(), s.pop(), c));
            }
        } catch (EmptyStackException e) {
            throw new Exception("Invalid entry.");
        }
        return (Math.abs(24 - s.peek()) < 0.001F);
    }

    float applyOperator(float a, float b, char c) {
        switch (c) {
            case '+':
                return a + b;
            case '-':
                return b - a;
            case '*':
                return a * b;
            case '/':
                return b / a;
            default:
                return Float.NaN;
        }
    }

    List<Integer> randomDigits() {
        Random r = new Random();
        List<Integer> result = new ArrayList<>(4);
        for (int i = 0; i < 4; i++)
            result.add(r.nextInt(9) + 1);
        return result;
    }

    List<Integer> getSolvableDigits() {
        List<Integer> result;
        do {
            result = randomDigits();

        } while (!isSolvable(result));
        return result;
    }

    boolean isSolvable(List<Integer> digits) {
        Set<List<Integer>> dPerms = new HashSet<>(4 * 3 * 2);
        permute(digits, dPerms, 0);

        int total = 4 * 4 * 4;
        List<List<Integer>> oPerms = new ArrayList<>(total);
        permuteOperators(oPerms, 4, total);

        StringBuilder sb = new StringBuilder(4 + 3);

        for (String pattern : patterns) {
            char[] patternChars = pattern.toCharArray();

            for (List<Integer> dig : dPerms) {
                for (List<Integer> opr : oPerms) {

                    int i = 0, j = 0;
                    for (char c : patternChars) {
                        if (c == 'n')
                            sb.append(dig.get(i++));
                        else
                            sb.append(ops.charAt(opr.get(j++)));
                    }

                    String candidate = sb.toString();
                    try {
                        if (evaluate(candidate.toCharArray())) {
                            solution = postfixToInfix(candidate);
                            return true;
                        }
                    } catch (Exception ignored) {
                    }
                    sb.setLength(0);
                }
            }
        }
        return false;
    }

    String postfixToInfix(String postfix) {
        class Expression {
            String op, ex;
            int prec = 3;

            Expression(String e) {
                ex = e;
            }

            Expression(String e1, String e2, String o) {
                ex = String.format("%s %s %s", e1, o, e2);
                op = o;
                prec = ops.indexOf(o) / 2;
            }
        }

        Stack<Expression> expr = new Stack<>();

        for (char c : postfix.toCharArray()) {
            int idx = ops.indexOf(c);
            if (idx != -1) {

                Expression r = expr.pop();
                Expression l = expr.pop();

                int opPrec = idx / 2;

                if (l.prec < opPrec)
                    l.ex = '(' + l.ex + ')';

                if (r.prec <= opPrec)
                    r.ex = '(' + r.ex + ')';

                expr.push(new Expression(l.ex, r.ex, "" + c));
            } else {
                expr.push(new Expression("" + c));
            }
        }
        return expr.peek().ex;
    }

    char[] infixToPostfix(char[] infix) throws Exception {
        StringBuilder sb = new StringBuilder();
        Stack<Integer> s = new Stack<>();
        try {
            for (char c : infix) {
                int idx = ops.indexOf(c);
                if (idx != -1) {
                    if (s.isEmpty())
                        s.push(idx);
                    else {
                        while (!s.isEmpty()) {
                            int prec2 = s.peek() / 2;
                            int prec1 = idx / 2;
                            if (prec2 >= prec1)
                                sb.append(ops.charAt(s.pop()));
                            else
                                break;
                        }
                        s.push(idx);
                    }
                } else if (c == '(') {
                    s.push(-2);
                } else if (c == ')') {
                    while (s.peek() != -2)
                        sb.append(ops.charAt(s.pop()));
                    s.pop();
                } else {
                    sb.append(c);
                }
            }
            while (!s.isEmpty())
                sb.append(ops.charAt(s.pop()));

        } catch (EmptyStackException e) {
            throw new Exception("Invalid entry.");
        }
        return sb.toString().toCharArray();
    }

    void permute(List<Integer> lst, Set<List<Integer>> res, int k) {
        for (int i = k; i < lst.size(); i++) {
            Collections.swap(lst, i, k);
            permute(lst, res, k + 1);
            Collections.swap(lst, k, i);
        }
        if (k == lst.size())
            res.add(new ArrayList<>(lst));
    }

    void permuteOperators(List<List<Integer>> res, int n, int total) {
        for (int i = 0, npow = n * n; i < total; i++)
            res.add(Arrays.asList((i / npow), (i % npow) / n, i % n));
    }
}

运行一下,可以输入‘s’输出算24的一个解:

代码语言:javascript
复制
使用如下数字计算24点: [9, 7, 4, 3]
(输入 'q' 退出, 输入's'获取一种解决方法 )
> s
3 * 9 + (4 - 7)
使用如下数字计算24点: [1, 7, 6, 3]
(输入 'q' 退出, 输入's'获取一种解决方法 )
>

启动Arthas

代码语言:javascript
复制
E:\develop>java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.1.7
[INFO] Found existing java process, please choose one and hit RETURN.
* [1]: 24756
  [2]: 12556 Game24Player

然后选择Game24Player作为我们要观察java进行,输入2,然后按回车键,可以看到如下信息:

Dashboard

输入dashboard,按回车/enter,会展示当前进程的信息,按ctrl+c可以中断执行。如:

代码语言:javascript
复制
ID                  NAME                                                       GROUP                                  PRIORITY            STATE              %CPU                TIME                INTERRUPTED        DAEMON
5                   Attach Listener                                            system                                 5                   RUNNABLE           0                   0:0                 false              true
11                  Common-Cleaner                                             InnocuousThreadGroup                   8                   TIMED_WAITING      0                   0:0                 false              true
3                   Finalizer                                                  system                                 8                   WAITING            0                   0:0                 false              true
2                   Reference Handler                                          system                                 10                  RUNNABLE           0                   0:0                 false              true
4                   Signal Dispatcher                                          system                                 9                   RUNNABLE           0                   0:0                 false              true
21                  Timer-for-arthas-dashboard-07e86b84-00bb-4e5d-86fe-e32ba43 system                                 10                  RUNNABLE           0                   0:0                 false              true
16                  arthas-shell-server                                        system                                 5                   TIMED_WAITING      0                   0:0                 false              true
20                  as-command-execute-daemon                                  system                                 10                  TIMED_WAITING      0                   0:0                 false              true
13                  job-timeout                                                system                                 5                   TIMED_WAITING      0                   0:0                 false              true
1                   main                                                       main                                   5                   RUNNABLE           0                   0:0                 false              false
14                  nioEventLoopGroup-2-1                                      system                                 10                  RUNNABLE           0                   0:0                 false              false
19                  nioEventLoopGroup-2-2                                      system                                 10                  RUNNABLE           0                   0:0                 false              false
15                  nioEventLoopGroup-3-1                                      system                                 10                  RUNNABLE           0                   0:0                 false              false
17                  pool-1-thread-1                                            system                                 5                   WAITING            0                   0:0                 false              false

通过thread命令来获取到进程的Main Class

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

输入thread 1 | grep 'main('

代码语言:javascript
复制
[arthas@12556]$ thread 1 | grep 'main('
    at app//Game24Player.main(Game24Player.java:14)

通过jad来反编译Main Class

代码语言:javascript
复制
[arthas@12556]$ jad Game24Player

ClassLoader:
+-jdk.internal.loader.ClassLoaders$AppClassLoader@15db9742
  +-jdk.internal.loader.ClassLoaders$PlatformClassLoader@1a4aa43e

Location:
/E:/develop/workspace/Test/bin/

/*
 * Decompiled with CFR.
 */
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EmptyStackException;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;
import java.util.Stack;

public class Game24Player {
    final String[] patterns = new String[]{"nnonnoo", "nnonono", "nnnoono", "nnnonoo", "nnnnooo"};
    final String ops;
    String solution;
    List<Integer> digits;

    public Game24Player() {
        this.ops = "+-*/^";
    }

    public static void main(String[] args) {
        new Game24Player().play();
    }

... ... 

... ...

三. 进阶使用

基础命令

代码语言:javascript
复制
help——查看命令帮助信息
cat——打印文件内容,和linux里的cat命令类似
echo–打印参数,和linux里的echo命令类似
grep——匹配查找,和linux里的grep命令类似
tee——复制标准输入到标准输出和指定的文件,和linux里的tee命令类似
pwd——返回当前的工作目录,和linux命令类似
cls——清空当前屏幕区域
session——查看当前会话的信息
reset——重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
version——输出当前目标 Java 进程所加载的 Arthas 版本号
history——打印命令历史
quit——退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
stop——关闭 Arthas 服务端,所有 Arthas 客户端全部退出
keymap——Arthas快捷键列表及自定义快捷键
代码语言:javascript
复制
[arthas@12556]$ session
 Name        Value
--------------------------------------------------
 JAVA_PID    12556
 SESSION_ID  c5eab2c5-4131-4b2a-849d-0d02124d185f
[arthas@12556]$
[arthas@12556]$
[arthas@12556]$ keymap
 Shortcut                                                   Description                                                Name
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 "\C-a"                                                     Ctrl + a                                                    beginning-of-line
 "\C-e"                                                     Ctrl + e                                                    end-of-line
 "\C-f"                                                     Ctrl + f                                                    forward-word
 "\C-b"                                                     Ctrl + b                                                    backward-word
 "\e[D"                                                     Left arrow                                                  backward-char
 "\e[C"                                                     Right arrow                                                 forward-char
 "\e[A"                                                     Up arrow                                                    history-search-backward
 "\e[B"                                                     Down arrow                                                  history-search-forward
 "\C-h"                                                     Ctrl + h                                                    backward-delete-char
 "\C-?"                                                     Ctrl + ?                                                    backward-delete-char
 "\C-u"                                                     Ctrl + u                                                    undo
 "\C-d"                                                     Ctrl + d                                                    delete-char
 "\C-k"                                                     Ctrl + k                                                    kill-line
 "\C-i"                                                     Ctrl + i                                                    complete
 "\C-j"                                                     Ctrl + j                                                    accept-line
 "\C-m"                                                     Ctrl + m                                                    accept-line
 "\C-w"                                                     Ctrl + w                                                    backward-delete-word
 "\C-x\e[3~"                                                "\C-x\e[3~"                                                 backward-kill-line
 "\e\C-?"                                                   "\e\C-?"                                                    backward-kill-word
 "\e[1~"                                                    "\e[1~"                                                     beginning-of-line
 "\e[4~"                                                    "\e[4~"                                                     end-of-line
 "\e[5~"                                                    "\e[5~"                                                     beginning-of-history
 "\e[6~"                                                    "\e[6~"                                                     end-of-history
 "\e[3~"                                                    "\e[3~"                                                     delete-char
 "\e[2~"                                                    "\e[2~"                                                     quoted-insert
 "\e[7~"                                                    "\e[7~"                                                     beginning-of-line
 "\e[8~"                                                    "\e[8~"                                                     end-of-line
 "\eOH"                                                     "\eOH"                                                      beginning-of-line
 "\eOF"                                                     "\eOF"                                                      end-of-line
 "\e[H"                                                     "\e[H"                                                      beginning-of-line
 "\e[F"                                                     "\e[F"                                                      end-of-line
[arthas@12556]$ pwd
E:\develop\workspace\Test
[arthas@12556]$
[arthas@12556]$
[arthas@12556]$ version
3.2.0
[arthas@12556]$ 

jvm相关

代码语言:javascript
复制
dashboard——当前系统的实时数据面板
thread——查看当前 JVM 的线程堆栈信息
jvm——查看当前 JVM 的信息
sysprop——查看和修改JVM的系统属性
sysenv——查看JVM的环境变量
vmoption——查看和修改JVM里诊断相关的option
perfcounter——查看当前 JVM 的Perf Counter信息
logger——查看和修改logger
getstatic——查看类的静态属性
ognl——执行ognl表达式
mbean——查看 Mbean 的信息
heapdump——dump java heap, 类似jmap命令的heap dump功能

如:

产生堆快照

代码语言:javascript
复制
[arthas@12556]$ heapdump
Dumping heap to C:\Users\WANGME~1\AppData\Local\Temp\heapdump2020-05-06-23-099555333078403039860.hprof...
Heap dump file created
[arthas@12556]$

class/classloader相关

代码语言:javascript
复制
sc——查看JVM已加载的类信息
sm——查看已加载类的方法信息
jad——反编译指定已加载类的源码
mc——内存编绎器,内存编绎.java文件为.class文件
redefine——加载外部的.class文件,redefine到JVM里
dump——dump 已加载类的 byte code 到特定目录
classloader——查看classloader的继承树,urls,类加载信息,使用classloader去getResource

如:

代码语言:javascript
复制
[arthas@12556]$ classloader
 name                                                  numberOfInstances  loadedCountTotal
 BootstrapClassLoader                                  1                  2389
 com.taobao.arthas.agent.ArthasClassloader             1                  2304
 jdk.internal.reflect.DelegatingClassLoader            7                  7
 jdk.internal.loader.ClassLoaders$AppClassLoader       1                  5
 jdk.internal.loader.ClassLoaders$PlatformClassLoader  1                  4
Affect(row-cnt:5) cost in 3 ms.
[arthas@12556]$  

monitor/watch/trace相关

请注意

请注意,这些命令,都通过字节码增强技术来实现的, 会在指定类的方法中插入一些切面来实现数据统计和观测, 因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件, 诊断结束要执行 stop 或将增强过的类执行 reset 命令。

代码语言:javascript
复制
monitor——方法执行监控
watch——方法执行数据观测
trace——方法内部调用路径,并输出方法路径上的每个节点上耗时
stack——输出当前方法被调用的调用路径
tt——方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测

如Monitor:多运行一下24点的程序,这样的话程序会去调用随机数方法,从而能被监控到:

代码语言:javascript
复制
[arthas@12556]$ monitor -c 5 Game24Player randomDigits
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 17 ms.
 timestamp            class         method        total  success  fail  avg-rt(ms)  fail-rate
----------------------------------------------------------------------------------------------
 2020-05-06 23:15:14  Game24Player  randomDigits  1      1        0     0.65        0.00%

 timestamp            class         method        total  success  fail  avg-rt(ms)  fail-rate
----------------------------------------------------------------------------------------------
 2020-05-06 23:15:19  Game24Player  randomDigits  7      7        0     0.16        0.00%

timestamp            class         method        total  success  fail  avg-rt(ms)  fail-rate                                                                                                                                            
----------------------------------------------------------------------------------------------                                                                                                                                          
 2020-05-06 23:15:24  Game24Player  randomDigits  0      0        0     0.00        0.00%                                                                                                                                               

 timestamp            class         method        total  success  fail  avg-rt(ms)  fail-rate                                                                                                                                           
----------------------------------------------------------------------------------------------                                                                                                                                          
 2020-05-06 23:15:29  Game24Player  randomDigits  0      0        0     0.00        0.00%     

又如stack

代码语言:javascript
复制
输出当前方法被调用的调用路径
很多时候我们都知道一个方法被执行,
但这个方法被执行的路径非常多,
或者你根本就不知道这个方法是从那里被执行了,此时你需要的是 stack 命令。

Arthas还支持很多的命令,可以通过如下链接阅读:

https://alibaba.github.io/arthas/advanced-use.html

四. 小结

经过上述的一些介绍,相信读者对Arthas有了一定的了解。

平时我们在开发的时候,可以通过monitor命令对方法的耗时进行监控;也可以通过thread命令的查找快速找到死锁的程序,再来看一个死锁的场景吧,一个简单的死锁程序

代码语言:javascript
复制


public class ThreadDeadLock implements Runnable{
    int a;
    int b;
    public ThreadDeadLock(int a, int b){
        this.a = a;
        this.b = b;
    }
    @Override
    public void run() {
        synchronized (Integer.valueOf(a)) {
            synchronized(Integer.valueOf(b)){
                System.out.println(a + b);
            }
        }
    }
    
    public static void main(String[] args) {
        for(int i=0; i<100; ++i){
            new Thread(new ThreadDeadLock(1, 2)).start();
            new Thread(new ThreadDeadLock(2, 1)).start();
        }
    }
}

运行一下,然后使用java -jar arthas-boot.jar启动Arthas,选择死锁程序的java进程,选择1,然后按回车;

代码语言:javascript
复制
E:\develop>java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.1.7
[INFO] Found existing java process, please choose one and hit RETURN.
* [1]: 20836 ThreadDeadLock
  [2]: 24756
1
[INFO] arthas home: C:\Users\wangmengjun\.arthas\lib\3.2.0\arthas
[INFO] Try to attach process 20836
[INFO] Attach process 20836 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'


wiki      https://alibaba.github.io/arthas
tutorials https://alibaba.github.io/arthas/arthas-tutorials
version   3.2.0
pid       20836
time      2020-05-06 23:31:43

然后,使用thread命令:

我们可以看到很多BLOCKED的线程,这些其实就是我们运行死锁程序导致的。然后针对某一个,比如id为180的线程进行分析:

代码语言:javascript
复制
[arthas@20836]$ thread 180
"Thread-168" Id=180 BLOCKED on java.lang.Integer@40e3a44f owned by "Thread-154" Id=166
    at app//ThreadDeadLock.run(ThreadDeadLock.java:13)
    -  blocked on java.lang.Integer@40e3a44f
    at java.base@12.0.2/java.lang.Thread.run(Thread.java:835)

Affect(row-cnt:0) cost in 37 ms.
[arthas@20836]$

我们很容易就知道BLOCKED是程序ThreadDeadLock导致的,很方便。

夜深了,就写到这里吧。有兴趣的同学可以到Arthas官网进一步了解。

参考

[1]. Arthas用户文档. https://alibaba.github.io/arthas/index.html

[2]. Arthas使用介绍.

https://www.cnblogs.com/qiaoyihang/p/10533672.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 孟君的编程札记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 快速安装
  • 二. 快速入门
  • 通过thread命令来获取到进程的Main Class
  • 三. 进阶使用
  • 四. 小结
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档