手把手教Arthas,不再怕排查线上问题了

news2024/11/16 18:35:58

Arthas是alibaba开源的java诊断工具,支持jdk6+,采用命令行交互模式,可以防败的定位和诊断线上的程序运行问题。官方文档:https://arthas.aliyun.com/doc/

一、Arthas使用场景

  1. 是否有一个全局视角来查看系统的运行状况?

  1. 为什么 CPU 又升高了,到底是哪里占用了 CPU ?

  1. 运行的多线程有死锁吗?有阻塞吗?

  1. 程序运行耗时很长,是哪里耗时比较长呢?如何监测呢?

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

  1. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

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

  1. 有什么办法可以监控到 JVM 的实时运行状态?

二、Arthas的使用

2.1下载

# github下载arthas
wget https://alibaba.github.io/arthas/arthas-boot.jar
# 或者 Gitee 下载
wget https://arthas.gitee.io/arthas-boot.jar

2.2 运行

在你要监控的应用启动成功之后,再启动Arthas程序

java -jar arthas-boot

选中需要监控的java程序pid

2.3 本次demo的代码

package cn.phlos.csdn.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Random;

/**
 * @ClassName: DemoController
 * @Author: lph
 * @Description:
 * @Date: 2023/1/5 22:07
 */
@RestController
public class DemoController {

    private static HashSet hashSet = new HashSet();

    /**
     * 模拟线程死锁,cup过高
     */
    @GetMapping("/thread")
    public void thread() {
        // 模拟 CPU 过高
        cpuHigh();
        // 模拟线程死锁
        deadThread();
        // 不断的向 hashSet 集合增加数据
        addHashSetThread();
    }

    /**
     * 模拟耗时
     */
    @GetMapping("/cost")
    public void cost(){
        for (int i = 0; i < 10; i++) {
            threadCost();
            hashSet.add(""+i);
        }
    }


    @GetMapping("/watch/{num}")
    public Integer watch(@PathVariable("num") Integer num){
        Random random = new Random();
        List<Integer> list = Arrays.asList(random.nextInt(100), random.nextInt(50));
        hashSet.add(""+1);
        return list.get(0)+list.get(1);
    }

    private void threadCost(){
        int nextInt = new Random().nextInt(20)+1;
        try {
            Thread.sleep(nextInt*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }



    /**
     * 不断的向 hashSet 集合添加数据
     */
    public void addHashSetThread() {
        // 初始化常量
        new Thread(() -> {
            int count = 0;
            while (true) {
                try {
                    hashSet.add("count" + count);
                    Thread.sleep(1000);
                    count++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void cpuHigh() {
        new Thread(() -> {
            while (true) {

            }
        }).start();
    }

    /**
     * 死锁
     */
    private void deadThread() {
        /** 创建资源 */
        Object resourceA = new Object();
        Object resourceB = new Object();
        // 创建线程
        Thread threadA = new Thread(() -> {
            synchronized (resourceA) {
                System.out.println(Thread.currentThread() + " get ResourceA");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resourceB");
                synchronized (resourceB) {
                    System.out.println(Thread.currentThread() + " get resourceB");
                }
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (resourceB) {
                System.out.println(Thread.currentThread() + " get ResourceB");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resourceA");
                synchronized (resourceA) {
                    System.out.println(Thread.currentThread() + " get resourceA");
                }
            }
        });
        threadA.start();
        threadB.start();

    }


}

或是下载该项目的jar包在本地上运行,按照步骤操作:

demo的jar的下载地址

三、监控功能

3.1 monitor:监控方法的执行情况

监控指定类中方法的执行情况、用来见识一个时间短指定方法的执行次数,成功次数、失败次数,耗时等这些信息

参数说明:

方法拥有一个明明参数[c:],意思是统计周期,为一个整数的类型

参数名称

参数说明

class-pattern

类名表达式匹配

method-pattern

方法名表达式匹配

condition-express

条件表达式

[E]

开启正则表达式匹配,默认为通配符匹配

[c:]

统计周期,默认值为 120 秒

[b]

方法调用之前计算 condition-express

案例1:

#监控接口的实现方法,并且3S更新一次状态
monitor cn.phlos.csdn.demo.DemoController cost -c 3

调用接口:localhost:8080/cost

就可以看到监控到这个方法的运行,每3秒打印一次

监控的维度说明

监控项

说明

timestamp

时间戳

class

Java 类

method

方法(构造方法、普通方法)

total

调用次数

success

成功次数

fail

失败次数

rt

平均 RT

fail-rate

失败率

3.2 watch:检查函数返回值

方法执行数据观测,让你能方便的观察到指定方法的调用情况。
能观察到的范围为: 返回值抛出异常入参,通过编写OGNL 表达式进行对应变量的查看。

参数说明:

watch 的参数比较多,主要是因为它能在 4 个不同的场景观察对象

参数名称

参数说明

class-pattern

类名表达式匹配

method-pattern

函数名表达式匹配

express

观察表达式,默认值:{params, target, returnObj}

condition-express

条件表达式

[b]

函数调用之前观察

[e]

函数异常之后观察

[s]

函数返回之后观察

[f]

函数结束之后(正常返回和异常返回)观察

[E]

开启正则表达式匹配,默认为通配符匹配

[x:]

指定输出结果的属性遍历深度,默认为 1,最大值是 4

这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写"{params,returnObj}",只要是一个合法的 ognl 表达式,都能被正常支持

特别说明

  • watch 命令定义了 4 个观察事件点,即 -b 函数调用前,-e 函数异常后,-s 函数返回后,-f 函数结束后

  • 4 个观察事件点 -b、-e、-s 默认关闭,-f 默认打开,当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出

  • 这里要注意函数入参和函数出参的区别,有可能在中间被修改导致前后不一致,除了 -b 事件点 params 代表函数入参外,其余事件都代表函数出参

  • 当使用 -b 时,由于观察事件点是在函数调用前,此时返回值或异常均不存在

  • watch 命令的结果里,会打印出location信息。location有三种可能值:AtEnter,AtExit,AtExceptionExit。对应函数入口,函数正常 return,函数抛出异常。

案例1:

#    查看方法执行的返回值
watch cn.phlos.csdn.demo.DemoController watch returnObj
#    观察DemoController类中watch方法出参和返回值,结果属性遍历深度为2
#    params:表示所有参数数组(因为不确定是几个参数)。
#    returnObject:表示返回值
watch cn.phlos.csdn.demo.DemoController watch "{params,returnObj}" -x 2

执行完命令,调用接口:localhost:8080/watch/2,即可看到数据

案例2:

#-b 查看方法执行前的参数
watch cn.phlos.csdn.demo.DemoController watch "{params,returnObj}" -x 2 -b

案例3

#查看方法中的属性
watch cn.phlos.csdn.demo.DemoController watch "{target}" -x 2 -b

案例4:

#检测方法在执行前-b、执行后-s的入参params、属性target和返回值returnObj
watch cn.phlos.csdn.demo.DemoController watch "{params,target,returnObj}" -x 2 -b -s -n 2

案例5:

#输入参数小于10的情况
watch cn.phlos.csdn.demo.DemoController watch "{params[0],target}" "params[0]<10"

执行:localh0ost:8080/watch/2、localhost:8080/watch/9、localhost:8080/watch/20

案例6:

#按照耗时进行过滤
watch cn.phlos.csdn.demo.DemoController watch "{params,returnObj}" "#cost>0.01" -x 2

3.3 trace:根据路径追踪,并记录消耗时间

方法内部调用路径,并输出方法路径上的每个节点上耗时

trace 命令能主动搜索 class-pattern/method-pattern 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。

参数说明

参数名称

参数说明

class-pattern

类名表达式匹配

method-pattern

方法名表达式匹配

condition-express

条件表达式

[E]

开启正则表达式匹配,默认为通配符匹配

[n:]

命令执行次数

#cost

方法执行耗时

这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写"{params,returnObj}",只要是一个合法的 ognl 表达式,都能被正常支持。
观察的维度也比较多,主要体现在参数 advice 的数据结构上。Advice 参数最主要是封装了通知节点的所有信息。

案例1:

# trace函数指定类的指定方法
trace cn.phlos.csdn.demo.DemoController cost

调用接口:localhost:8080/cost

案例2:

# 执行一次后退出
trace cn.phlos.csdn.demo.DemoController cost -n 1

案例3:

#默认情况下,trace不会包含jdk里的函数调用,如果希望trace jdk里的函数。
#需要显式设置--skipJDKMethod false。
trace --skipJDKMethod false cn.phlos.csdn.demo.DemoController cost

案例4:

#据调用耗时过滤,trace大于100ms的调用路径
trace cn.phlos.csdn.demo.DemoController cost '#cost > 100'
只会展示耗时大于 10ms 的调用路径,有助于在排查问题的时候,只关注异常情
  • 是不是很眼熟,没错,在 JProfiler 等收费软件中你曾经见识类似的功能,这里你将可以通过命令就能打印出指定调用路径。 友情提醒下,trace 在执行的过程中本身是会有一定的性能开销,在统计的报告中并未像 JProfiler 一样预先减去其自身的统计开销。所以这统计出来有些许的不准,渲染路径上调用的类、方法越多,性能偏差越大。但还是能让你看清一些事情的。

  • [1127.5045ms] 的含义,1127.5045 的含义是:当前节点在当前步骤的耗时,单位为毫秒

  • [0,0,0ms,11]xxx:yyy() [throws Exception],对该方法中相同的方法调用进行了合并,0,0,0ms,11 表示方法调用耗时,min,max,total,countthrows Exception 表明该方法调用中存在异常返回

  • 这里存在一个统计不准确的问题,就是所有方法耗时加起来可能会小于该监测方法的总耗时,这个是由于 Arthas 本身的逻辑会有一定的耗时

案例5

trace 命令只会 trace 匹配到的函数里的子调用,并不会向下 trace 多层。因为 trace 是代价比较贵的,多层 trace 可能会导致最终要 trace 的类和函数非常多。

可以用正则表匹配路径上的多个类和函数,一定程度上达到多层 trace 的效果

# 可以用正则表匹配路径上的多个类和函数,一定程度上达到多层trace的效果。
trace -E com.test.ClassA|org.test.ClassB method1|method2|method3

案例6:

# 使用 --exclude-class-pattern 参数可以排除掉指定的类
trace javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter

3.4 stack:输出当前方法被调用的调用路径

很多时候我们都知道一个方法被执行,但这个方法被执行的路径非常多,或者你根本就不知道这个方法是从那里被执行了,此时你需要的是 stack 命令。

参数说明

参数名称

参数说明

class-pattern

类名表达式匹配

method-pattern

方法名表达式匹配

condition-express

条件表达式

[E]

开启正则表达式匹配,默认为通配符匹配

[n:]

执行次数限制

这里重点要说明的是观察表达式,观察表达式的构成主要由 ognl 表达式组成,所以你可以这样写"{params,returnObj}",只要是一个合法的 ognl 表达式,都能被正常支持。

案例1:

#获取cost的调用路径
stack cn.phlos.csdn.demo.DemoController cost

调用:localhost:8080/cost

案例2:

#    条件表达式来过滤,第0个参数的值小于0,-n表示获取2次
stack cn.phlos.csdn.demo.DemoController watch 'params[0]<0' -n 2

调用:localhost:8080/watch/-9、localhost:8080/watch/-9

案例3:

#    据执行时间来过滤,耗时大于100毫秒
stack cn.phlos.csdn.demo.DemoController cost '#cost>100'

3.5 tt:时间隧道,记录多个请求

方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测

watch 虽然很方便和灵活,但需要提前想清楚观察表达式的拼写,这对排查问题而言要求太高,因为很多时候我们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜测。

这个时候如果能记录下当时方法调用的所有入参和返回值、抛出的异常会对整个问题的思考与判断非常有帮助。

于是乎,TimeTunnel 命令就诞生了。

参数说明

参数名称

参数说明

-t

记录某一个方法在一个时间段中的调用

-l

显示所有已经记录的列表

-n 次数

只记录多少次

-s 表达式

搜索表达式

-i 索引号

查看指定索引号的详细调用信息

-p

重新调用:指定的所有号时间碎片

  • 命令参数解析

  • -t

tt 命令有很多个主参数,-t 就是其中之一。这个参数的表明希望记录下类 *Test 的 print 方法的每次执行情况。

  • -n 3

当你执行一个调用量不高的方法时可能你还能有足够的时间用 CTRL+C 中断 tt 命令记录的过程,但如果遇到调用量非常大的方法,瞬间就能将你的 JVM 内存撑爆。

此时你可以通过 -n 参数指定你需要记录的次数,当达到记录次数时 Arthas 会主动中断 tt 命令的记录过程,避免人工操作无法停止的情况。

案例1:

#    最基本的使用来说,就是记录下当前方法的每次调用环境现场。
tt -t cn.phlos.csdn.demo.DemoController cost
  • 表格字段说明

表格字段

字段解释

INDEX

时间片段记录编号,每一个编号代表着一次调用,后续 tt 还有很多命令都是基于此编号指定记录操作,非常重要。

TIMESTAMP

方法执行的本机时间,记录了这个时间片段所发生的本机时间

COST(ms)

方法执行的耗时

IS-RET

方法是否以正常返回的形式结束

IS-EXP

方法是否以抛异常的形式结束

OBJECT

执行对象的hashCode(),注意,曾经有人误认为是对象在 JVM 中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体

CLASS

执行的类名

METHOD

执行的方法名

案例2:

#对现有记录进行检索
tt -l

案例3:

# 需要筛选出 `cost` 方法的调用信息
tt -s 'method.name=="cost"'

案例4:

#    查看某条记录详细信息
tt -i 1000

案例5:

#重做一次调用
tt -i 1000 -p

当你稍稍做了一些调整之后,你可能需要前端系统重新触发一次你的调用,此时得求爷爷告奶奶的需要前端配合联调的同学再次发起一次调用。而有些场景下,这个调用不是这么好触发的。

tt 命令由于保存了当时调用的所有现场信息,所以我们可以自己主动对一个 INDEX 编号的时间片自主发起一次调用,从而解放你的沟通成本。此时你需要 -p 参数。通过 --replay-times 指定 调用次数,通过 --replay-interval 指定多次调用间隔(单位 ms, 默认 1000ms)

你会发现结果虽然一样,但调用的路径发生了变化,由原来的程序发起变成了 Arthas 自己的内部线程发起的调用了。

四、基础命令

序号

基础命令

功能

1

help

显示所有arthas命令,每个命令都可以使用-h的参数,显示它的参数信息

2

cat

显示文本文件内容

3

grep

对内容进行过滤,只显示关心的行

4

pwd

显示当前的工作路径

5

session

显示当前连接的会话ID

6

reset

重置arthas增强的类

7

version

显示当前arthas的版本号

8

history

查看历史命令

9

cls

清除屏幕

10

quit

退出当前的会话

11

stop

结束arthas服务器,退出所有的会话

12

keymap

显示所有的快捷键

4.1 help:查看命令帮助信息

可以查看当前 arthas 版本支持的指令,或者查看具体指令的使用说明

[help 指令]的等同于[指令 -help],都是查看具体指令的使用说明。

参数说明

参数名称

参数说明

不接参数

查询当前 arthas 版本支持的指令以及指令描述

[name:]

查询具体指令的使用说明

4.2 cat:打印文件内容

打印文件内容,和 linux 里的 cat 命令类似。

4.3 grep:管道命令

类似传统的grep命令。
 USAGE:
   grep [-A <value>] [-B <value>] [-C <value>] [-h] [-i] [-v] [-n] [-m <value>] [-e] [--trim-end] pattern

 SUMMARY:
   grep command for pipes.

 EXAMPLES:
  sysprop | grep java
  sysprop | grep java -n
  sysenv | grep -v JAVA
  sysenv | grep -e "(?i)(JAVA|sun)" -m 3  -C 2
  sysenv | grep JAVA -A2 -B3
  thread | grep -m 10 -e  "TIMED_WAITING|WAITING"

 WIKI:
   https://arthas.aliyun.com/doc/grep

 OPTIONS:
 -A, --after-context <value>                                                    Print NUM lines of trailing context)
 -B, --before-context <value>                                                   Print NUM lines of leading context)
 -C, --context <value>                                                          Print NUM lines of output context)
 -h, --help                                                                     this help
 -i, --ignore-case                                                              Perform case insensitive matching.  By default, grep is case sensitive.
 -v, --invert-match                                                             Select non-matching lines
 -n, --line-number                                                              Print line number with output lines
 -m, --max-count <value>                                                        stop after NUM selected lines)
 -e, --regex                                                                    Enable regular expression to match
     --trim-end                                                                 Remove whitespaces at the end of the line
 <pattern>                                                                      Pattern

案例:

sysprop |grep "java"    #    只显示包含java字符串的行系统属性
sysprop |grep "java" -n     # 显示行号
sysprop |grep "java" -n -m10    #    显示行号,只显示10行
thread | grep -e "o+"    #    使用正则表达式,显示包含2个o字符的线程信息

4.4 pwd:打印当前的工作目录

返回当前的工作目录,和 linux 命令类似

4.5 session:查看当前会话的信息

如果配置了 tunnel server,会追加打印 代理 id、tunnel 服务器的 url 以及连接状态。
如果使用了 staturl 做统计,会追加显示 statUrl 地址。

4.6 reset:重置增强后类

重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端stop时会重置所有增强过的类
reset Test     #    还原指定类
reset *List    #    还原所有以List结尾的类
reset          #    还原所有的类

案例:

#查询方法耗时
trace cn.phlos.csdn.demo.DemoController cost
#所有的类
reset 

4.7 version:查看Arthas 版本号

输出当前目标 Java 进程所加载的 Arthas 版本号

4.8 history:打印历史命令

历史指令会通过一个名叫 history 的文件持久化,所以 history 指令可以查看当前 arthas 服务器的所有历史命令,而不仅只是当前次会话使用过的命令

参数说明

参数名称

参数说明

[c:]

清空历史指令

[n:]

显示最近执行的 n 条指令

#查看最近执行的3条指令
history 3

#清空指令
history -c

4.9 cls:清空当前屏幕区域

非终端模式下使用 cls 指令,会提示"Command 'cls' is only support tty session."。

4.10 quit:退出当前 Arthas 客户端

只是退出当前 Arthas 客户端,Arthas 的服务器端并没有关闭,所做的修改也不会被重置。
其他 Arthas 客户端不受影响。等同于 exitlogoutq三个指令。

4.11 stop:关闭 Arthas 服务端,所有 Arthas 客户端全部退出

关闭 Arthas 服务器之前,会重置掉所有做过的增强类。但是用 redefine 重加载的类内容不会被重置

4.12 keymap:查看当前的Arthas快捷键映射表

默认的快捷键如下:

快捷键

快捷键说明

命令名称

命令说明

"\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"

键盘左方向键

backward-char

光标向前移动一个字符

"\e[C"

键盘右方向键

forward-char

光标向后移动一个字符

"\e[B"

键盘下方向键

next-history

下翻显示下一个命令

"\e[A"

键盘上方向键

previous-history

上翻显示上一个命令

"\C-h"

ctrl + h

backward-delete-char

向后删除一个字符

"\C-?"

ctrl + shift + /

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

自动补全,相当于敲TAB

"\C-j"

ctrl + j

accept-line

结束当前行,相当于敲回车

"\C-m"

ctrl + m

accept-line

结束当前行,相当于敲回车

"\C-w"

backward-delete-word

"\C-x\e[3~"

backward-kill-line

"\e\C-?"

backward-kill-word

  • 任何时候 tab 键,会根据当前的输入给出提示

  • 命令后敲 - 或 -- ,然后按 tab 键,可以展示出此命令具体的选项

后台异步命令相关快捷键

  • ctrl + c: 终止当前命令

  • ctrl + z: 挂起当前命令,后续可以 bg/fg 重新支持此命令,或 kill 掉

  • ctrl + a: 回到行首

  • ctrl + e: 回到行尾

五、JVM相关命令

序号

命令

功能说明

1

dashboard

仪表板,可以显示:线程,内存,堆栈,GC,Runtime等信息

2

thread

显示线程信息

3

jvm

与JVM相关的信息

4

sysprop

显示系统属性信息,也可以修改某个属性

5

sysenv

查看JVM环境变量的值

6

vmoption

查看JVM中选项,可以修改

7

getstatic

获取静态成员变量

8

ognl

执行一条ognl表达式,对象图导航语言

5.1 dashboard:实时数据面板

当前系统的实时数据面板,按 ctrl+c 退出
当运行在 Ali-tomcat 时,会显示当前 tomcat 的实时信息,如 HTTP 请求的 qps, rt, 错误数, 线程池信息等等。

参数说明

参数名称

参数说明

[i:]

刷新实时数据的时间间隔 (ms),默认 5000ms

[n:]

刷新实时数据的次数

字段说明:

  • ID: Java 级别的线程 ID,注意这个 ID 不能跟 jstack 中的 nativeID 一一对应。

  • NAME: 线程名

  • GROUP: 线程组名

  • PRIORITY: 线程优先级, 1~10 之间的数字,越大表示优先级越高

  • STATE: 线程的状态

  • CPU%: 线程的 cpu 使用率。比如采样间隔 1000ms,某个线程的增量 cpu 时间为 100ms,则 cpu 使用率=100/1000=10%

  • DELTA_TIME: 上次采样之后线程运行增量 CPU 时间,数据格式为秒

  • TIME: 线程运行总 CPU 时间,数据格式为分:秒

  • INTERRUPTED: 线程当前的中断位状态

  • DAEMON: 是否是 daemon 线程

JVM 内部线程

Java 8 之后支持获取 JVM 内部线程 CPU 时间,这些线程只有名称和 CPU 时间,没有 ID 及状态等信息(显示 ID 为-1)。 通过内部线程可以观测到 JVM 活动,如 GC、JIT 编译等占用 CPU 情况,方便了解 JVM 整体运行状况。

  • 当 JVM 堆(heap)/元数据(metaspace)空间不足或 OOM 时,可以看到 GC 线程的 CPU 占用率明显高于其他的线程。

  • 当执行trace/watch/tt/redefine等命令后,可以看到 JIT 线程活动变得更频繁。因为 JVM 热更新 class 字节码时清除了此 class 相关的 JIT 编译结果,需要重新编译。

JVM 内部线程包括下面几种:

  • JIT 编译线程: 如 C1 CompilerThread0, C2 CompilerThread0

  • GC 线程: 如GC Thread0, G1 Young RemSet Sampling

  • 其它内部线程: 如VM Periodic Task Thread, VM Thread, Service Thread

5.2 thread:查看当前线程信息

查看当前线程信息,查看线程的堆栈

参数说明

参数名称

参数说明

id

线程 id

[n:]

指定最忙的前 N 个线程并打印堆栈

[b]

找出当前阻塞其他线程的线程

[i <value>]

指定 cpu 使用率统计的采样间隔,单位为毫秒,默认值为 200

[--all]

显示所有匹配的线程

thread                   #    显示所有线程的信息
thread 1                 #    显示1号线程的运行堆栈
thread -b                #    查看阻塞的线程信息
thread -n 3              #    查看最忙的3个线程,并打印堆栈
thread -i 1000 -n 3      #    指定采样时间间隔,每过1000毫秒采样,显示最占时间的3个线程
thread --state WAITING   #    查看处于等待状态的线程(WAITING、BLOCKED)

案例1:

thread        #    查看线程状态
thread -b     #    查看阻塞的线程信息

执行接口:localhost:8080/thread

案例2:

#查看处于等待状态的线程(WAITING、BLOCKED)
thread --state WAITING   

5.3 jvm:查看当前 JVM 信息

THREAD 相关

  • COUNT: JVM 当前活跃的线程数

  • DAEMON-COUNT: JVM 当前活跃的守护线程数

  • PEAK-COUNT: 从 JVM 启动开始曾经活着的最大线程数

  • STARTED-COUNT: 从 JVM 启动开始总共启动过的线程次数

  • DEADLOCK-COUNT: JVM 当前死锁的线程数

文件描述符相关

  • MAX-FILE-DESCRIPTOR-COUNT:JVM 进程最大可以打开的文件描述符数

  • OPEN-FILE-DESCRIPTOR-COUNT:JVM 当前打开的文件描述符数

5.4 sysprop:查看/修改属性

查看当前 JVM 的系统属性(System Property)

案例:

sysprop                        #    查看所有属性
sysprop java.version           #    查看单个属性,支持通过tab补全

sysprop user.country         #查看
sysprop user.country US      #修改

5.5 sysenv:查看jvm环境属性

查看当前 JVM 的环境属性(System Environment Variables)
sysenv        # 查看所有环境变量
sysenv USER   # 查看单个环境变量

5.6 vmpotion:查看,更新 VM 诊断相关的参数

查看,更新 VM 诊断相关的参数
vmoption                        #    查看所有的选项
vmoption PrintGCDetails         #    查看指定的选项
vmoption PrintGCDetails true    #    更新指定的选项

5.7 getstatic:获取类的静态属性

#    语法
getstatic 类名 属性名

#显示DemoController的静态属性hashSet
getstatic cn.phlos.csdn.demo.DemoController hashSet

5.8 ognl:执行ognl表达式

参数说明

参数名称

参数说明

express

执行的表达式

[c:]

执行表达式的 ClassLoader 的 hashcode,默认值是 SystemClassLoader

[classLoaderClass:]

指定执行表达式的 ClassLoader 的 class name

[x]

结果对象的展开层次,默认值 1

案例:

调用静态函数

ognl '@java.lang.System@out.println("hello")'

获取静态类的静态字段:

ognl '@cn.phlos.csdn.demo.DemoController@hashSet'

执行多行表达式,赋值给临时变量,返回一个List

#    计算value1、value2值,并存在List集合中
ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'

六、类和类加载器(class、classLoad)相关命令

序号

命令

功能说明

1

sc

Search Class 查看运行中的类信息

2

sm

Search Method 查看类中方法的信息

3

jad

反编译字节码为源代码

4

mc

Memory Compile 将源代码编译成字节码

5

redefine

将编译好的字节码文件加载到jvm中运行

6

dump

加载类的 bytecode 到特定目录

7

classloader

查看类加载信息

6.1 sc:查看 JVM 已加载的类信息

“Search-Class” 的简写,这个命令能搜索出所有已经加载到 JVM 中的 Class 信息,这个命令支持的参数有 [d][E][f] [x:]

参数说明

参数名称

参数说明

class-pattern

类名表达式匹配

method-pattern

方法名表达式匹配

[d]

输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的 ClassLoader 等详细信息。

如果一个类被多个 ClassLoader 所加载,则会出现多次

[E]

开启正则表达式匹配,默认为通配符匹配

[f]

输出当前类的成员变量信息(需要配合参数-d 一起使用)

[x:]

指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出

[c:]

指定 class 的 ClassLoader 的 hashcode

[classLoaderClass:]

指定执行表达式的 ClassLoader 的 class name

[n:]

具有详细信息的匹配类的最大数量(默认为 100)

[cs <arg>]

指定 class 的 ClassLoader#toString() 返回值。长格式[classLoaderStr <arg>]

class-pattern 支持全限定名,如 com.taobao.test.AAA,也支持 com/taobao/test/AAA 这样的格式,这样,我们从异常堆栈里面把类名拷贝过来的时候,不需要在手动把/替换为.啦。
sc 默认开启了子类匹配功能,也就是说所有当前类的子类也会被搜索出来,想要精确的匹配,请打开options disable-sub-class true开关

sc cn.phlos.*                                #    模糊搜索,demo包下所有的类
sc -d cn.phlos.csdn.demo.DemoController      #    打印类的详细信息

6.2 sm:查看已加载类的方法信息

“Search-Method” 的简写,这个命令能搜索出所有已经加载了 Class 信息的方法信息。
sm 命令只能看到由当前类所声明 (declaring) 的方法,父类则无法看到。

参数说明

参数名称

参数说明

class-pattern

类名表达式匹配

method-pattern

方法名表达式匹配

[d]

展示每个方法的详细信息

[E]

开启正则表达式匹配,默认为通配符匹配

[c:]

指定 class 的 ClassLoader 的 hashcode

[classLoaderClass:]

指定执行表达式的 ClassLoader 的 class name

[n:]

具有详细信息的匹配类的最大数量(默认为 100)

sm java.lang.String                        #     显示String类加载的方法
sm cn.phlos.csdn.demo.DemoController       #     查看方法信息
sm -d cn.phlos.csdn.demo.DemoController    #     查看方法信息(详细信息-d)

6.3 jad:反编译指定已加载类的源码

jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于你理解业务逻辑;

  • 在 Arthas Console 上,反编译出来的源码是带语法高亮的,阅读更方便

  • 当然,反编译出来的 java 代码可能会存在语法错误,但不影响你进行阅读理解

参数说明

参数名称

参数说明

class-pattern

类名表达式匹配

[c:]

类所属 ClassLoader 的 hashcode

[classLoaderClass:]

指定执行表达式的 ClassLoader 的 class name

[E]

开启正则表达式匹配,默认为通配符匹配

# 反编译MathGame方法
jad cn.phlos.csdn.demo.DemoController
# 反编绎时只显示源代码(排除ClassLoader信息)。
# 默认情况下,反编译结果里会带有ClassLoader信息,通过--source-only选项,可以只打印源代码。方便和mc/redefine命令结合使用。
jad --source-only cn.phlos.csdn.demo.DemoController
# 反编译到指定文件中
jad --source-only cn.phlos.csdn.demo.DemoController > Demo.java
# 只反编译DemoController类型中cost方法
jad cn.phlos.csdn.demo.DemoController cost

6.4 mc:编译java代码生成class文件

Memory Compiler/内存编译器,编译.java文件生成.class。

#在内存中编译 Test.java为Test.class
mc /root/Demo.java  
#可以通过-d命令指定输出目录                                   
mc -d /root/output /root/Demo.java     

6.5 redefine:加载外部的.class文件

推荐使用 retransform 命令
  • redefine 的 class 不能修改、添加、删除类的 field 和 method,包括方法参数、方法名称及返回值

  • 如果 mc 失败,可以在本地开发环境编译好 class 文件,上传到目标系统,使用 redefine 热加载 class

  • 目前 redefine 和 watch/trace/jad/tt 等命令冲突,以后重新实现 redefine 功能会解决此问题

注意, redefine 后的原来的类不能恢复,redefine 有可能失败(比如增加了新的 field),参考 jdk 本身的文档

1. reset命令对 redefine的类无效。如果想重置,需要 redefine原始的字节码。
2. redefine命令和 jad/ watch/ trace/ monitor/ tt等命令会冲突。执行完 redefine之后,如果再执行上面提到的命令,则会把 redefine的字节码重置。 原因是 jdk 本身 redefine 和 Retransform 是不同的机制,同时使用两种机制来更新字节码,只有最后修改的会生效。

redefine 的限制

  • 不允许新增加 field/method

  • 正在跑的函数,没有退出不能生效,比如下面新增加的System.out.println,只有run()函数里的会生效

参数说明

参数名称

参数说明

[c:]

ClassLoader 的 hashcode

[classLoaderClass:]

指定执行表达式的 ClassLoader 的 class name

# 1. 使用jad反编译DemoController输出到/root/Hello.java
jad --source-only cn.phlos.csdn.demo.DemoController > /root/Hello.java

# 2.按上面的代码编辑完毕以后,使用mc内存中对新的代码编译
mc /root/Hello.java -d /root

# 3.使用redefine命令加载新的字节码
redefine /root/Hello.class

6.6 dump:已加载类的 bytecode 到特定目录

参数说明

参数名称

参数说明

class-pattern

类名表达式匹配

[c:]

类所属 ClassLoader 的 hashcode

[classLoaderClass:]

指定执行表达式的 ClassLoader 的 class name

[d:]

设置类文件的目标目录

[E]

开启正则表达式匹配,默认为通配符匹配

#    把String类的字节码文件保存到~/logs/arthas/classdump/目录下
dump java.lang.String
#    把demo包下所有的类的字节码文件保存到~/logs/arthas/classdump/目录下
dump cn.*

6.7 classloader:查看类加载信息

查看 classloader 的继承树,urls,类加载信息

classloader 命令将 JVM 中所有的 classloader 的信息统计出来,并可以展示继承树,urls 等。

可以让指定的 classloader 去 getResources,打印出所有查找到的 resources 的 url。对于ResourceNotFoundException比较有用。

参数说明

参数名称

参数说明

[l]

按类加载实例进行统计

[t]

打印所有 ClassLoader 的继承树

[a]

列出所有 ClassLoader 加载的类,请谨慎使用

[c:]

ClassLoader 的 hashcode

[classLoaderClass:]

指定执行表达式的 ClassLoader 的 class name

[c: r:]

用 ClassLoader 去查找 resource

[c: load:]

用 ClassLoader 去加载指定的类

案例1:

#默认按类加载器的类型查看统计信息
classloader

案例2:

#按类加载器的实例查看统计信息,可以看到类加载的hashCode
classloader -l

案例3:

#查看ClassLoader的继承树
classloader -t

案例4:

#    通过类加载器的hashcode,查看此类加载器实际所在的位置
classloader -c 349da6dd

案例5:

#使用ClassLoader去查找指定资源resource所在的位置
classloader -c 349da6dd -r META-INF/MANIFEST.MF

案例6:

# 使用ClassLoader(该类的hashcode)去加载类
classloader -c 70dea4e --load java.lang.String

classloader命令主要作用有哪些?

  • 显示所有类加载器的信息

  • 获取某个类加载器所在的jar包

  • 获取某个资源在哪个jar包中

  • 加载某个类

七、 Web Console(web控制台)

Arthas 目前支持 Web Console,用户在 attach 成功之后,可以直接访问:http://127.0.0.1:8563/

可以填入 IP,远程连接其它机器上的 arthas。

默认情况下,arthas 只 listen 127.0.0.1,所以如果想从远程连接,则可以使用 --target-ip参数指定 listen 的 IP,更多参考-h的帮助说明。 注意会有安全风险,考虑 Arthas Tunnel 的方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/150127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

六种方式,教你在SpringBoot初始化时搞点事情!

前言 在实际工作中总是需要在项目启动时做一些初始化的操作&#xff0c;比如初始化线程池、提前加载好加密证书....... 那么经典问题来了&#xff0c;这也是面试官经常会问到的一个问题&#xff1a;有哪些手段在Spring Boot 项目启动的时候做一些事情&#xff1f; 方法有很多…

卷积层里的多输入多输出通道、池化层

多输入多通道每个通道都有一个卷积核&#xff0c;结果是所有通道卷积结果的和。无论有多少输入通道&#xff0c;到目前为止我们只用到单输出通道。可以有多个三维卷积核&#xff0c;每个核生成一个输出通道。输出通道数是卷积层的超参数。每个输入通道有独立的二维卷积核&#…

为什么JavaScript这么难学啊?

前言 觉得Js难其实是很正常的&#xff0c;首先这证明你在某些知识点上没有理解透彻&#xff0c;JS挺多的知识点点其实是比较抽象的&#xff0c;比如闭包、原型和原型链等&#xff0c;其次便是不会变通运用&#xff0c;这主要是敲代码熟练度的问题&#xff0c;所以我针对你这种…

架构运维篇(六):MySQL 8.0启用BinLog 支持

上一篇&#xff1a;架构运维篇&#xff08;五&#xff09;&#xff1a;Centos7/Linux中安装RocketMQ 最新线上的项目终于到多个数据执行的问题&#xff0c;找了团队DBA发现云上的MySQL 默认是没有启用BinLog支持。 小编研究了一下很简单&#xff0c;不过中间也遇到一些坑可以给…

结构重参数化(Structural Re-Parameters)PipLine

文章目录BASICSstrcutural Inception算法思想算法核心算法架构Re-Parameter四部曲&#xff1a;ACNetACNet原理ACNet分析涨点原因推理阶段融合机制Re-Parameter四部曲&#xff1a;RepVGGRepVGG原理RepVGG分析RepVGG BlockStructural Re-Parameters融合conv2d和BN融合1x1conv转换…

【一文讲通】样本不均衡问题解决--下

1欠采样、过采样欠采样&#xff1a;减少多数类的数量&#xff08;如随机欠采样、NearMiss、ENN&#xff09;。过采样&#xff1a;尽量多地增加少数类的的样本数量&#xff08;如随机过采样、以及2.1.2数据增强方法&#xff09;&#xff0c;以达到类别间数目均衡。还可结合两者做…

地址解析协议ARP

目录地址解析协议ARP1、流程2、动态与静态的区别3、ARP协议适用范围地址解析协议ARP 如何从IP地址找出其对应的MAC地址&#xff1f; 1、流程 ARP高速缓存表 当主机B要给主机C发送数据包时&#xff0c;会首先在自己的ARP高速缓存表中查找主机C的IP地址所对应的MAC地址&#xf…

Linux常用命令——lsblk命令

在线Linux命令查询工具 lsblk 列出块设备信息 补充说明 lsblk命令用于列出所有可用块设备的信息&#xff0c;而且还能显示他们之间的依赖关系&#xff0c;但是它不会列出RAM盘的信息。块设备有硬盘&#xff0c;闪存盘&#xff0c;cd-ROM等等。lsblk命令包含在util-linux-ng…

ES报文辅助生成工具-JavaFX

此程序为基于 Java8 开发的 JavaFX Maven 工程&#xff0c;是 Java 组装ElasticSearch请求报文工具的辅助 Java 代码生成工具&#xff0c;方便开发者快速编写代码。现学现用&#xff0c;写得不好。 工具界面 代码 pom.xml <project xmlns"http://maven.apache.org/P…

Android:URLEncoder空格被转码为“+”号

Android前段和后端接口交互时&#xff0c;经常会遇到特殊字符&#xff0c;比如表情、特殊标点等&#xff0c;这样在Url中是无法识别的&#xff0c;需要进行转码&#xff0c;后端进行解码交互。 但当使用URLEncoder时&#xff0c;会发现字符串中的空格被转换成“”号&#xff0…

客服系统即时通讯IM开发(四)网站实现实时在线访客列表【唯一客服】网站在线客服系统...

在使用我的客服系统时&#xff0c;如果引入了我的js &#xff0c;就可以实时看到网站上的所有访客了 使用 WebSocket 技术来实现实时通信。 在访客登录或退出时&#xff0c;向指定客服的 WebSocket 客户端发送消息。例如&#xff0c;你可以在访客登录时&#xff0c;向指定客服…

测试用例的设计? 万能公式

万能公式(必背)&#xff1a;功能测试性能测试界面测试兼容性测试易用性测试安全测试功能测试 &#xff1a;可能来自于需求文档&#xff0c;也可能来自生活经验性能测试 &#xff1a;功能没有问题不代表性能是ok的&#xff0c;性能往往体现在一些极端情况界面测试 &#xff1a;颜…

Prometheus-基于Consul的自动注册

一、背景介绍 如果我们的物理机有很多&#xff0c;不管是基于"file_sd_config"还是"kubernetes_sd_config"&#xff0c;我们都需要手动写入目标target及创建目标service&#xff0c;这样才能被prometheus自动发现&#xff0c;为了避免重复性工作过多&#…

【182】Java8利用二叉查找树实现Map

本文利用二叉查找树写了一个Map&#xff0c;用来保存键值对。 二叉查找树的定义 二叉查找树又名二叉搜索树&#xff0c;英文名称是 Binary Search Tree&#xff0c;缩写BST。 二叉排序树&#xff0c;英文名称是 Binary Sorted Tree&#xff0c;缩写BST。 二叉查找树、二叉搜…

excel实用技巧:如何构建多级下拉菜单

使用数据有效性制作下拉菜单对大多数小伙伴来说都不陌生&#xff0c;但说到二级和三级下拉菜单大家可能就不是那么熟悉了。什么是二级和三级下拉菜单呢&#xff1f;举个例子&#xff0c;在一个单元格选择某个省后&#xff0c;第二个单元格选项只能出现该省份所属的市&#xff0…

vue-router原理简单实现

vue-router简单实现 初步预习 动态路由 获取id方式 第一种强依赖路由 第二种父传子方式&#xff08;推荐&#xff09; 嵌套路由 相同的头和尾&#xff0c;默认index&#xff0c;替换为detail 编程时导航 this.$router.push() this.$router.repleace() this.$router.g…

吊炸天,springboot的多环境配置一下搞明白了!

1、 使用springboot的profile命名规则profile用于多环境的激活和配置&#xff0c;用来切换生产&#xff0c;测试&#xff0c;本地等多套不通环境的配置。如果每次去更改配置就非常麻烦&#xff0c;profile就是用来切换多环境配置的。在Spring Boot框架中&#xff0c;使用Profil…

漏洞优先级排序的六大关键因素

当我们谈及开源漏洞时&#xff0c;我们会发现其数量永远处于增长状态。根据安全公司 Mend 研究发现&#xff0c;在 2022 年前九个月发现并添加到其漏洞数据库中的开源漏洞数量比 2021 年增加了 33%。该报告从 2022 年 1 月到 2022 年 9 月对大约 1,000 家北美公司进行了代表性抽…

一篇文章解决C语言操作符

我的主页&#xff1a;一只认真写代码的程序猿本文章是关于C语言操作符的讲解收录于专栏【C语言的学习】 目录 1、算术操作符 2、赋值操作符 3、关系操作符 4、条件操作符&#xff08;三目&#xff09; 5、逻辑操作符 6、单目操作符 7、移位操作符 8、位操作符 9、逗号…

使用Docker+Nignx部署vue项目

文章目录一、前言二、vue项目打包三、nginx基本介绍①nginx常用的功能&#xff1a;②nginx默认的主题配置文件解读③nginx目录解读三、docker内部署nginx①拉取nginx镜像②创建数据持久化目录☆☆☆③创建需要映射进去的文件④运行nginx四、大工告成最近&#xff08;之前&#…