下载地址:
curl -0 https:alibaba.github.io/arthas/arthas-boot.jar
实际下载地址:
curl -0 https://alibaba.github.io/arthas/arthas-boot.jar --output arthas-boot.jar
安装前的准备:必须运行一个Java进程,或者是运行一个Java程序,或者是启动你的idea,都算是启动了一个Java进程,Arthas启动的时候会检测这个进程,否则就会报下面的问题。
当我们随便打开一个idea项目,并且不用启动项目,然后进入包含我们下载jar包的目录之后,我们执行安装命令。
当我们执行完之后就会下载Arthas的安装文件,然后进入Arthas的交互界面
上图中我们可以清晰的看到arthas的安装目录,一个是.arthas目录,另外一个是logs目录会有arthas的缓存和日志记录文件。以上就是arthas安装好之后的界面。
总结:
1、下载安装arthas-boot.jar包
2、执行arthas-boot.jar包,前提是必须有Java进行程在运行,第一次执行这个jar包会自动从服务器商下载arthas大小是11mb左右。
跟windows安装一模一样。
Linux如何启动一个Java进程,其实很简单,可以启动一下我们安装的Tomcat服务器软件,只要是一个Java进程就好了。
rm -rf ~/arthas/
rm -rf -/logs/arthas
ls可以查看对应目录下的所有文件,Linux目录下的文件.开头的都是隐藏的,所以我们可以通过ls -a的方式进行查看。
直接把两个文件夹进行删除即可。
1、执行一个jar包(这个jar包得类似于服务,不能执行就结束)
2、通过arthas来attach(粘附)
3、进行几种常用的命令操作
下载地址:
curl -0 https://alibaba.github.io/arthas/arthas-demo.jar
实际下载地址:
curl -0 https://alibaba.github.io/arthas/arthas-demo.jar --output arthas-demo.jar
上图中是arthas中启动的时候检测到的在Java虚拟机当中运行的进程。
启动并粘附成功:
启动但是没有粘附成功:
这是因为我们的arthas已经粘附过另外一个Java进程了,我们可以将上一个停掉或者切换一个端口号重新进行粘附:
Java -jar arthas-boot.jar --telnet-port 9998 --http-port -1
总结:
1、粘附一个进程需要启动一个进程。
2、粘附一个进程报错就换端口号
3、可以通过浏览器的方式去使用arthas:
1、dashboard仪表盘
2、通过thread命令来获取arthas -demo进程的main class
3、通过jad来反编译Main Class
4、watch
四部分组成,最后一个部分是Linux系统的信息,使用q或者ctrl+c进行退出,arthas当中的清屏是cls而不是Linux当中的clear
查看当前粘附Java进程当中所有的线程,和dashboard看的内容基本差不多
通过:
thread 1
查看具体某一线程的信息:
jad 包名.类名
里边的行号和源代码是严格对应的,他会把所有的源码反编译过来,一共分为三个部分:
1、加载这个类所用到的类加载器
2、这个类所在的jar包(真正意义详细到jar包)
3、这个类的源码
watch demo.MathGame primeFactors returnObj
这个监视的命令是实施的,方法被调用一次就会被监视一次,将对应的信息打印到控制台。这种命令下也是基于q或者是ctrl + c退出
exit 或者 quit都可以,不过这两种退出之后,端口还是黏贴上的,如果想要彻底退出的话,需要使用stop命令。
总结:
1、如何启动arthas
java -jar arthas-boot.jar
2、命令的作用
dashboard仪表盘,显示内存使用情况
thead显示Java虚拟机某个监听Java进程所有的线程信息
jad是用来反编译类的
watch监视某个方法或者类的执行情况
quit、exit、stop的区别
作用:
打印文件内容与Linux系统中的cat命令类似
如果没有写路径,则显示当前目录下的文件
作用:
匹配查找,和Linux当中的grep类似,但是只能用于管道作用,为了过滤某些文件当中的字符串
参数列表:
sysprop命令,显示系统Java虚拟机,arthas当中的命令,单独使用sysproop:
与grep配合使用:(只显示带有具体字符串的数据)
想带行号,并且还最多显示10行怎么办:
正则表达式检索带数字的
显示包含system字符串的10行信息:
使用正则表达式,显示包含两个o字符串的线程信息:
返回当前的工作目录,Linux,arthas,windows通用展示如下:
arthas的清屏操作,Linux当中的清屏是clear
总结:
help:显示所有的arthas密令帮助信息
cat:显示文件内容,绝对相对路径均可
grep:过滤一些信息,只保留我们关心的信息
pwd:显示当前工作目录
cls:清屏
作用:查看当前会话信息
效果:我们知道,arthas可以认为是启动了一个服务器,咱们每一个服务的跟踪和粘附都是在这个arthas内部创建了一个会话,当前我们通过session查看会话信息
Java_pid指的是启动的这个程序的进程号,session_id指的是会话id
加入我们现在再启动一个arthas实例:
我们在启动一个arthas实例粘附原有的程序,就会发现session_id是不一样的
arthas可以基于多个实例和对应的会话粘附到同一个Java进程上
作用:重置增强类,将被Arthas增强过的类全部还原,Arthas服务关闭是会重置所有增强过的类。
还原指定类
reset Test
还原所有以List结尾的类
reset *List
还原所有的类
reset
作用:关闭Arthas会话,或者说客户端,多个客户端的时候不会影响其他的客户端。
作用:关闭所有Arthas会话,所有的Arthas客户端都会进行关闭,使用不当会把别人的实例关闭掉
就是一些输入命令时的快捷键,最好用的就是ctrl+u清空当前行,ctrl+i自动补全
总结:
1、session:显示当前会话信息,粘附进程id和会话id
2、reset:重置对类的增强
3、version:显示当前arthas版本号
4、quit:退出当前会话,不会影响其他会话
5、stop:退出arthas服务器,所有的arthas都会停止
6、keymap:获取快捷键
作用:
显示当前Jvm实时数据的一个面板,按q或者ctrl+c退出
效果:
每隔一段时间刷新一次,dashboard我么可以看到线程、内存、垃圾回收期、虚拟机、tomcat的一些信息:
作用:
查看当前系统的jvm中的具体的线程堆栈信息
参数说明:
查看某个线程编号的线程情况:
thread id
thread -n 3
使用线程命令查看死锁的情况:
1、首先启动某一个非可执行jar包当中的某一个类:
Java -cp mydemo.jar com.xxx.xxx.xxx.Test
2、启动之后,多来几次来进行尝试死锁,死锁自后启动arthas粘附这个死锁进程,然后通过命令查看阻塞线程
thrad -b
每隔多少毫秒进行采样,查看最繁忙的线程,繁忙程度是按照CPU的使用率测算的:
查看不同状态的线程
thread --state WATING
作用:
查看当前运行的jvm参数:
最上边是一些运行时的信息,机器名字,Java虚拟机开启时间,类路径,以及所有的类路径,包括库所在的路径,包括JVM启动的时候的一些参数,包括已经加载的类的各种数量,包括内存管理器和内存的一些信息,包括线程先关的一些信息,后台线程的一些信息。
sysprop
sysprop user.country US
sysprop user.country CN
总结:
1、dashboard:显示线程、内存、GC、系统环境信息
2、thread:线程信息
3、jvm:与jvm虚拟机相关的信息
4、sysprop:显示系统信息,也可以修改相关属性
作用:查看当前JVM的环境属性(System Environment variables),查看当前虚拟机的属性
命令:
sysenv
sysenv USER
作用:查看、更新jvm诊断参数
命令:
vmoption 查看所有jvm诊断参数
vmoption HeapDumpBeforeFullGC 查看某一个jvm诊断参数
vmoption printGCDetails true 修改某一个jvm诊断参数
作用:查看类的静态属性
语法:
getstatis 类名 属性名
示例:
[arthas@39]$ getstatic com.cpu.pureq.action.utils.PutaskPageQueryCreateUtils logger
field: logger
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.yonyou.yuncai.cpu.pureq.action.utils.PutaskPageQueryCreateUtils],
level=null,
effectiveLevelInt=@Integer[40000],
parent=@Logger[Logger[com.yonyou.yuncai.cpu.pureq.action.utils]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
Affect(row-cnt:1) cost in 44 ms.
[arthas@39]$
Search Class
作用:查看jvm已经加载的类的信息,这个命令可以搜索出所有已经加载到JVM中的类的Class信息,sc默认开启了子类匹配功能,当前类的子类也会被匹配出来,想要精确匹配,需要打开optionsdisable-sub-class true开关
举例:
sc demo.* 搜索demo包下所有的类
sc demo.MathGame 搜索这个类
sc demo.MathGame -d 查看这个类的详细信息
sc demo.MathGame -df 查看这个类的详细信息、成员变量
Search Method
作用:查看已加载类的方法信息
查看已加载类的方法信息,这个命令可以搜索出所有已经加载了Class信息的方法信息,sm命令只能看到当前类所声明的方法,父类不能看到。
举例:
sm demo.MathGame
sm java.lang.String
sm java.lang.String -d
jad demo.MathGame 反编译这个类
jad java.lang.String 反编译String这个类
jad --source-only java.lang.String 只显示这个源码,不显示类加载器
jad java.lnag.String trim 只反编译这一个方法
反编译出来的代码不仅仅包含源代码,还包含类加载器、此类所在的包等相关信息。
作用:在内存中把源代码编译成字节码文件,也就是在内存中把Java文件编译成class文件。
示例:
mc /root/Hello.java 进行内存编译,并将class文件输出默认目录
mc -d /root/bbb /root/Hello.java 进行内存编译,并将该文件输出到root目录下
作用:把新生成的字节码文件在内存中执行,注意redefine后的原来的类不恢复,redefine有可能失败,比如新增了field,reset命令对redefine命令无效,如果想重置,需要redefine原始的字节码,redefine命令和jad/watch/trace/monitor/tt等命令会冲突,执行完redefine之后,如果在执行上边提到的命令会把redefine的字节码重置
redefine命令的限制:
1、不允许新增field或者method
2、正在跑的函数,没有退出不能生效。
案例:结合jad/mc命令使用
步骤:
1、使用jad命令反编译demo.MathGame输出到/root/MathGame.java
## > 是重定向到文件的意思
jad --source-only demo.MathGame > /root/MathGame.java
jad --source-only demo.MathGame 这个命令是反编译到屏幕的意思
2、按上边的代码编辑完毕之后,使用mc内存进行编译
mc /root/MathGame.java -d /root
3、使用redefine命令加载新的字节码
redefine /root/demo/MathGame.class
过程:
我们在反编译的类当中加了一行打印语句
成功之后的结果:
之前是没有一道一道的横线的
总结一下:
1、jad:反编译字节码文件为Java文件
2、mc:在内存中编译源代码为字节码
3、redefine:将编译好的字节码文件重新加载到内存中
作用:将已加载的字节码文件保存到特定目录:logs/arthas/classdump/
将当前正在运行的这个类进行一个备份,上边是他的默认保存路径。
举例:
1、把String类的字节码文件保存到默认目录下
dump java.lang.String
2、把demo包下所有的目录保存到默认目录下
dump demo.*
作用:获取类加载器的信息
1、classloader命令将jvm当中所有的类加载器信息统计出来,并可以展示继承树,urls等
2、可以让指定的classloader去getResource,打印出来所有的resource的URL。对于ResourceNotFoundException异常比较有用。
举例:
classloader 查看所有的类加载器的统计信息
calssloader -l 类加载器统计信息,多了hash值和父类加载器信息
classloader -a 展示类加载器加载的所有的类
classloader -c 指定一个类加载器的hashcode去查看它所在的jar包
classloader -c hash -r 资源名 用classloader去查找resource
classloader -c hash -load java.lang.String 指定类加载去去加载对应的字节码文件
这种方式更多展示了类加载器的hashcode和类加载器的父类加载器,对于parent属性是null的说明这个类加载器是一个顶层的类加载器,使用时c语言写的类加载器。Java的类加载器是有几成关系的。
可以找到这个类加载器所在的jar包
可以找到这个类或者文件所在的地方
总结:
classloader命令的主要作用:
1、显示所有类加载器的信息
2、获取某个类加载器所在的jar包
3、获取某个资源所在的jar包
4、加载某个类到jvm内存中
作用:监视一个类当中的方法的执行情况。monitor命令不是一个实时返回的命令,实时返回的命令是输入之后就返回,而非实时返回的命令是输入之后不断等待Java进程返回信息,直到用户输入Ctrl+C停止
过5秒监控一次,类demo.MathGame中的primeFactors方法
monitor -c 5 demo.MathGame primeFactors
默认过120秒之后才会返回一次信息
monitor demo.MathGame primeFactors
每过5秒就会返回一次信息:
小结:
1、监视统计一个时间段内指定方法的执行情况(次数、成功率、耗时情况)
作用:实时观察指定方法的调用情况,可以看到返回值、抛出异常、入参,通过编写OGNL表达式进行对应变量的查看。
查看方法的入参和返参,方法执行完毕之后查看。
watch demo.MathGame primeFactors "{params,returnObj}" -x 2
只看方法的入参,方法执行前进行查看,所以方法返回值为null
watch demo.MathGame primeFactors "{params,returnObj}" -x 2 -b
查看方法运行前,该方法所在对象的全部的属性
watch demo.MathGame primeFactors "target" -x 2 -b
因为设置的x=2所以可以看到属性下两级的值。
示例4:
查看方法运行前,该方法所在对象的某一个属性的属性
watch demo.MathGame primeFactors "target.illegalArgumentCount" -x 2 -b
查看方法运行前/后,该方法所在对象的某一个属性的属性,-n 2表示执行两次
watch demo.MathGame primeFactors "{params,target,returnObj}" -x 2 -b -s -n 2
arthas 任何一个命令都可以进行-h ,例如watch -h可以看到对应的命令提示。
示例6:
查看方法某一个具体入参,并且对入参进行条件过滤,将满足条件的显示出来
watch demo.MathGame primeFactors "{params[0],target}" "params[0] < 0" -x 2
我们可以看到,我们没有指定深度,当前的深度都是默认1,只能看到对象的地址值。
小结:
1、-b监视方法执行前的情况
2、-e监视出现异常的情况
3、-s监视执行成功的情况
4、-f默认,监视执行完毕的情况
5、-n执行多少次,-x监视深度
作用:方法内部调用命令,对一个方法的内部调用路径进行追踪,输出方法路径上的每节点耗时。
trace命令能主动追踪方法上的对应调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路
观察表达式的构成主要有OGNL表达式组成例如:“{param,returnObj}”
很多时候,我们只想看某个方法的rt大于某个时间的trace结果,我们可以通过方法执行的耗时进行过滤,也就是当执行时间超过100ms的时候才会输出结果
watch stack trace这三个命令都支持#cost耗时条件过滤
在Arthas当中大量支持了OGNL表达式
trace指定类的指定方法
trace demo.MathGame run
trace指定类的指定方法,监视2次就停止
trace demo.MathGame run -n 2
trace指定类的指定方法,监视2次就停止,JDK方法也要追踪(默认是跳过的)
trace --skipJDKMethod false demo.MathGame run -n 2
trace demo.MathGame run -n 2 ‘#cost > 1’
[arthas@63]$ trace com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction loadDashBoard -n 1
Press Q or Ctrl+C to abort.
Affect(class count: 2 , method count: 1) cost in 1006 ms, listenerId: 2
`---ts=2022-08-18 22:25:03;thread_name=http-nio-8080-exec-2;id=120;is_daemon=true;priority=5;TCCL=iuap.yms.server.classloader.YmsParallelWebappClassLoader@7f884918
`---[2216.783166ms] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:loadDashBoard()
+---[0.01% 0.215999ms ] org.slf4j.Logger:info() #81
+---[0.02% 0.445901ms ] com.alibaba.fastjson.JSONObject:parseObject() #84
+---[0.00% 0.088611ms ] com.aaa.bbb.cpu.pureq.action.utils.PureqPageQueryParamCompareUtils:convertRequestParam2Parameters() #85
+---[15.25% 338.154043ms ] com.aaa.bbb.cpu.pureq.action.utils.PureqPageQueryPrepareUtils:prepareCriteria() #88
+---[0.00% 0.094084ms ] com.aaa.bbb.cpu.pureq.action.utils.PureqPageQueryCreateUtils:createSearchParamsStartingWith() #91
+---[0.00% 0.031497ms ] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:getPageBean() #95
+---[0.01% 0.159316ms ] org.slf4j.Logger:info() #98
+---[61.32% 1359.328694ms ] com.aaa.bbb.cpu.pureq.action.PureqPageDoQueryAction:doBillQuery() #100
+---[0.00% 0.040455ms ] com.aaa.cpu.commons.domain.response.ServiceResponse:getResult() #102
+---[0.00% 0.026328ms ] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:addAllowanceFlag() #102
+---[0.00% 0.007792ms ] com.aaa.cpu.commons.domain.response.ServiceResponse:getResult() #104
+---[0.00% 0.010035ms ] com.aaa.cpu.commons.domain.PageBean:getData() #104
+---[3.26% 72.373614ms ] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:addReqPersonCode() #104
+---[0.01% 0.166502ms ] org.slf4j.Logger:info() #107
+---[4.82% 106.786907ms ] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:processStatsticInfo() #110
+---[0.00% 0.016997ms ] com.aaa.cpu.domain.entity.enterprise.EnterprisePOJO:getTenantid() #111
+---[0.00% 0.014588ms ] com.alibaba.fastjson.JSONObject:put() #111
+---[0.00% 0.019813ms ] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:fillContractCount() #113
+---[0.01% 0.167445ms ] org.slf4j.Logger:info() #116
+---[4.41% 97.868586ms ] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:queryHasERP() #119
+---[0.01% 0.166919ms ] org.slf4j.Logger:info() #121
+---[0.00% 0.063295ms ] org.slf4j.Logger:info() #123
+---[0.04% 0.996233ms ] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:cacheStatistic() #125
+---[0.00% 0.013147ms ] com.aaa.cpu.commons.domain.response.ServiceResponse:getResult() #128
+---[0.00% 0.007173ms ] com.aaa.cpu.commons.domain.PageBean:getData() #128
+---[10.74% 238.057462ms ] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:addPureqPrice() #128
+---[0.00% 0.027636ms ] com.aaa.cpu.commons.domain.PageBean:getPageNo() #129
+---[0.00% 0.018844ms ] com.aaa.cpu.commons.domain.PageBean:getPageSize() #129
+---[0.00% 0.01571ms ] com.aaa.cpu.commons.domain.response.ServiceResponse:getResult() #129
`---[0.00% 0.077261ms ] com.aaa.bbb.cpu.pureq.action.PureqQueryPageAction:getResult() #129
Command execution times exceed limit: 1, so command will exit. You can set it with -n option.
总结:
trace命令查看的就是一个方法底下所有的被调用方法(儿子方法)的调用顺序、行号、耗时、是否有异常等信息。
作用:输出当前方法被调用的整个路径
很多时候,我们都知道一个方法被执行,但是这个方法被执行的路径有很多,或者你根本就不知道这个方法是从那里来的,这个时候需要的就是stack命令。
举例:
stack demo.MathGame primeFactors
第0个参数值小于0,监听两次
stack demo.MathGame primeFactors 'prams[0] < 0 ' -n 2
花费时间大于0.5毫秒的
stack demo.MathGame primeFactors '#cost > 0.5' -n 2
作用:
记录下指定方法每次调用的入参和返回信息,并能对这些不同时间下调用的信息进行观测
举例:
tt -t demo.MathGame primeFactors
条件表达式:
Arthas似乎很难区分出方法的重载,假如我们只需要观察特定参数,但是tt命令却都记录了下来。
条件表达式也都是用OGNL表达式来写,核心的判断对象依然是Advice对象。除了tt命令之外,watch,trace,stack也都支持条件表达式。
解决方法重载
tt -t *Test print param.length ==1
tt -t *Test print param[1] instanceof Integer
tt -t *Test print params[0].mobile == "13989838402"
检索调用记录,也就是在现有的结果中进行搜索
tt -l 检索调动记录
tt -s 'method.name == "primeFactors"'
查看特定的索引记录
tt -i 1008
现在我改了点代码,想要再次检测,这个时候,我们想要通知前端再次帮忙触发这种情况,然而tt命令中我们可以直接进行触发。让某些调用重新调用一次。
把这个时间片(索引号)重新调用一次,默认是隔一秒调用一次
tt -i 1008 -p
把这个时间片(索引号)重新调用三次
tt -i 1008 -p --replay-times 3
重新调用三次、间隔两秒,单位是毫秒
tt -i 1008 -p --replay-times 3 --replay-interval 2000
小结:
记录指定方法每次调用的入参和返回值,后期还可以对这些记录进行观测,或者重新调用。
作用:生成火焰图
profiler命令支持生成应用热点的火焰图,本质上通过不断的采样,然后把采集到的采样结果生成火焰图,基本运行结构是profiler命令
启动profiler
Started [cpu] profiling
默认情况下生成的是CPU的火焰图,即事件event为CPU,可以用–event参数来指定。
profiler list查看所有支持的事件
profiler getSamples 查询获取到的样本的数量,随着事件的推移,获取到的样本的数量就越来越多。
显示当前获取的状态
profiler status
profiler stop