系统稳定性—Java诊断工具Arthas使用

一、概述

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

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

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

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

二、安装与使用

2.1 使用arthas-boot(推荐)

下载arthas-boot.jar,然后用java -jar的方式启动:

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

打印帮助信息:

java -jar arthas-boot.jar -h
  • 如果下载速度比较慢,可以使用aliyun的镜像:java -jar arthas-boot.jar --repo-mirror aliyun --use-http

2.2 使用as.sh

Arthas支持在Linux/Unix/Mac等平台上一键安装,请复制以下内容,并粘贴到命令行中,敲回车执行即可:

curl -L https://arthas.aliyun.com/install.sh | sh

上述命令会下载启动脚本文件as.sh到当前目录,你可以放在任何地方或将其加入到$PATH中。

直接在shell下面执行./as.sh,就会进入交互界面。

也可以执行./as.sh -h来获取更多参数信息。

三、核心监视功能

3.1 monitor

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

参数说明

方法拥有一个命名参数 [c:],意思是统计周期(cycle of output),拥有一个整型的参数值

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
[E] 开启正则表达式匹配,默认为通配符匹配
[c:] 统计周期,默认值为120秒

3.2 watch

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

参数说明

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

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
express 观察表达式
condition-express 条件表达式
[b] 在方法调用之前观察before
[e] 在方法异常之后观察 exception
[s] 在方法返回之后观察 success
[f] 在方法结束之后(正常返回和异常返回)观察 finish
[E] 开启正则表达式匹配,默认为通配符匹配
[x:] 指定输出结果的属性遍历深度,默认为 1

3.3 trace

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

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

参数说明

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式
[E] 开启正则表达式匹配,默认为通配符匹配
[n:] 命令执行次数
#cost 方法执行耗时

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

观察的维度也比较多,主要体现在参数advice的数据结构上。Advice参数最主要是封装了通知节点的所有信息。

3.4 stack

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

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

参数说明

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式
[E] 开启正则表达式匹配,默认为通配符匹配
[n:] 执行次数限制

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

观察的维度也比较多,主要体现在参数advice的数据结构上。Advice参数最主要是封装了通知节点的所有信息。

使用例子

stack demo.MathGame primeFactors

据条件表达式来过滤

stack demo.MathGame primeFactors 'params[0]<0' -n 2

据执行时间来过滤

stack demo.MathGame primeFactors '#cost>5'

3.5 tt(Time Tunnel)

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

watch虽然很方便和灵活,但需要提前想清楚观察表达式的拼写,这对排查问题而言要求太高,因为很多时候我们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜测。
这个时候如果能记录下当时方法调用的所有入参和返回值、抛出的异常会对整个问题的思考与判断非常有帮助。
于是乎,TimeTunnel命令就诞生了。
作用:记录指定方法每次调用的入参和返回值,并后期还可以对这些信息进行观测

参数说明

tt的参数 说明
-t 记录某个方法在一个时间段中的调用
-l 显示所有已经记录的列表
-n 次数 只记录多少次
-s 表达式 搜索表达式
-i 索引号 查看指定索引号的详细调用信息
-p 重新调用:指定的索引号时间碎片

四、项目中使用

4.1 jad

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

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

  • Arthas Console上,反编译出来的源码是带语法高亮的,阅读更方便
  • 当然,反编译出来的java代码可能会存在语法错误,但不影响你进行阅读理解

4.1.1 参数说明

参数名称 参数说明
class-pattern 类名表达式匹配
[c:] 类所属 ClassLoader 的 hashcode
[classLoaderClass:] 指定执行表达式的 ClassLoader 的 class name
[E] 开启正则表达式匹配,默认为通配符匹配

4.1.2 使用参考

  • 反编译java.lang.String: jad java.lang.String
  • 反编译时只显示源代码:默认情况下,反编译结果里会带有ClassLoader信息,通过--source-only
    选项,可以只打印源代码。方便和mc /retransform命令结合使用。jad --source-only demo.MathGame
  • 反编译指定的函数:jad demo.MathGame main
  • 反编译时不显示行号:--lineNumber
    参数默认值为true,显示指定为false则不打印行号。jad demo.MathGame main --lineNumber false
  • 反编译时指定ClassLoader

当有多个ClassLoader都加载了这个类时,jad命令会输出对应ClassLoader</span>实例的<span class="pre">hashcode,然后你只需要重新执行jad命令,并使用参数-c <hashcode>就可以反编译指定ClassLoader加载的那个类了;

jad org.apache.log4j.Logger对于只有唯一实例的ClassLoader还可以通过--classLoaderClass指定class name,使用起来更加方便:

--classLoaderClass的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而-c <hashcode>是动态变化的。

五、常用命令

5.1 help

查看帮助信息

5.2 version

查看Arthas版本信息

5.3 cat

显示文件下,文本内容。
如果没有写路径,则显示当前目录下的文件

5.4 grep

在文件中搜索关键字

参数

参数列表 作用
-n 显示行号
-i 忽略大小写查找
-m 行数 最大显示行数,要与查询字符串一起使用
-e “正则表达式” 使用正则表达式查找

5.5 pwd

查看当前目录

5.6 session

显示当前会话信息

5.7 reset

重置增强类,将被Arthas增强过的类全部还原,Arthas服务端关闭时会重置所有增强过的类。

5.8 history

显示历史命令

5.9 quit

退出当前Arthas客户端,其它客户端不受影响。
exit或者quit命令可以退出Arthas

退出Arthas之后,还可以再次用java -jar arthas-boot.jar来连接。

exit/quit命令只是退出当前sessionarthas server还在目标进程中运行。想完全退出Arthas,可以执行stop命令。

5.10 stop

关闭Arthas服务端,所有Arthas服务端全部退出。

5.11 keymap

显示当前快捷键

六、JVM命令

6.1 dashboard

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

参数说明

参数名称 参数说明
[i:] 刷新实时数据的时间间隔 (ms),默认5000ms
[n:] 刷新实时数据的次数

6.2 thread

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

参数说明

参数名称 参数说明
id 线程id
[n:] 指定最忙的前N个线程并打印堆栈
[b] 找出当前阻塞其他线程的线程
[i value] 指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200
[--all] 显示所有匹配的线程

cpu使用率是如何统计出来的?

这里的cpu使用率与linux命令top -H -p <pid> 的线程%CPU类似,一段采样间隔时间内,当前JVM里各个线程的增量cpu
时间与采样间隔时间的比例。

6.2.1 工作原理

  • 首先第一次采样,获取所有线程的CPU时间(调用的是java.lang.management.ThreadMXBean#getThreadCpuTime()
    sun.management.HotspotThreadMBean.getInternalThreadCpuTimes()接口)
  • 然后睡眠等待一个间隔时间(默认为200ms,可以通过-i指定间隔时间)
  • 再次第二次采样,获取所有线程的CPU时间,对比两次采样数据,计算出每个线程的增量CPU时间
  • 线程CPU使用率 = 线程增量CPU时间 / 采样间隔时间 * 100%

注意:这个统计也会产生一定的开销(JDK这个接口本身开销比较大),因此会看到as
的线程占用一定的百分比,为了降低统计自身的开销带来的影响,可以把采样间隔拉长一些,比如5000毫秒。

另外一种查看Java进程的线程cpu

使用率方法:可以使用show-busy-java-threads

这个脚本

6.2.2 使用参考

thread -n,支持一键展示当前最忙的前N个线程并打印堆栈:thread -n 3

  • 没有线程ID,包含[Internal]表示为JVM内部线程。
  • cpuUsage为采样间隔时间内线程的CPU使用率,与[dashboard]命令的数据一致。
  • deltaTime为采样间隔时间内线程的增量CPU时间,小于1ms时被取整显示为0ms。
  • time 线程运行总CPU时间。

注意:线程栈为第二采样结束时获取,不能表明采样间隔时间内该线程都是在处理相同的任务。建议间隔时间不要太长,可能间隔时间越大越不准确。可以根据具体情况尝试指定不同的间隔时间,观察输出结果。

当没有参数时,显示第一页线程的信息,默认按照CPU增量时间降序排列,只显示第一页数据。

thread –all,显示所有匹配的线程,显示所有匹配线程信息,有时需要获取全部JVM的线程数据进行分析。
thread id,显示指定线程的运行堆栈,如thread 1
thread
-b,找出当前阻塞其他线程的线程,有时候我们发现应用卡住了,通常是由于某个线程拿住了某个锁,并且其他线程都在等待这把锁造成的。为了排查这类问题,arthas
提供了thread -b,一键找出那个罪魁祸首。thread b

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

thread -i,指定采样时间间隔

  • thread -i 1000:统计最近1000ms内的线程CPU时间。
  • thread -n 3 -i 1000:列出1000ms内最忙的3个线程栈

thread –state,查看指定状态的线程:thread --state WAITING

6.3 jvm

查看当前JVM信息

6.4 sysprop

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

6.5 sysenv

查看当前JVM的环境属性(System Environment Variables)

6.6 vmtool

vmtool利用JVMTI接口,实现查询内存对象,强制GC等功能。

6.7 mc

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

mc /tmp/Test.java

可以通过-c参数指定classloader:

mc -c 327a647b /tmp/Test.java

也可以通过<span class="pre">--classLoaderClass</span>参数指定ClassLoader:

mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader

可以通过-d命令指定输出目录:

mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java

编译生成.class文件之后,可以结合retransform命令实现热更新代码。

6.8 retransform

加载外部的.class文件,retransform热更新jvm`已加载的类。

retransform /tmp/Test.class
retransform -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class

6.9 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)
posted @ 2022-04-24 11:25  夏尔_717  阅读(808)  评论(0)    收藏  举报