1. arthas运行
1.1下载运行
下载地址: https://alibaba.github.io/arthas/arthas-boot.jar
运行java -jar arthas-boot.jar
安装以后出现这两个目录:user home下面的.arthas和logs/arthas目录
ls -a会出现所有的隐藏文件
arthas安装好的目录:/Users/jlgl/.arthas/lib/3.6.2/arthas
我千寻的电脑的启动位置:/Users/dingyawu/.arthas/lib/3.6.2/arthas
linux机器安装arthas
unzip arthas-packaging-3.6.0-bin.zip
linux 上执行 ./install-local.sh
随后粘附即可
1.2 arthas删除
rm -rf .arthas/
rm -rf logs/arthas
rm 后边跟的是目录
1.3. 启动arthas并尝试粘附
1.进入安装好的目录/Users/jlgl/.arthas/lib/3.6.2/arthas,
启动命令:java -jar arthas-boot.jar,会检测java虚拟机中的所有进程,相当于命令jps-l
粘附成功的标识如图1
2. 如果端口号被占用,也可以通过以下命令换成另一个端口号执行
java -jar arthas-boot.jar --telnet-port 9998 --http-port -1
3. 访问图2的地址,也支持浏览器访问
4.图3代表指定主类启动
2. Arthas命令
2.1. 常见命令总结
| 命令 | 解释 |
|---|---|
| thread -n 3 | 展示当前最忙的前 3 个线程并打印堆栈 |
| thread 1 | 显示 1 号线程的运行堆栈 |
| thread -i 1000 -n 3 | 指定采样时间间隔,每过1000毫秒采样,显示最占时间的3个线程 |
| thread -b | 找出当前阻塞其他线程的线程,有时候我们发现应用卡住了, 通常是由于某个线程拿住了某个锁, 并且其他线程都在等待这把锁造成的。 为了排查这类问题, arthas提供了thread -b, 一键找出那个罪魁祸首。 |
| thread --state WAITING | 查看处于等待状态的线程 |
| jvm | 查看当前 JVM 的信息 |
| sysprop | 查看和修改JVM的系统属性 |
| sysprop java.version | 查看单个属性,支持通过tab补全 |
| sysenv | 查看jvm环境变量 |
| vmoptions | 查看jvm参数 |
| sc -df demo.MathGame | 打印类的详细信息 |
| monitor -c 5 demo.MathGame primeFactors | 过5秒统计一次,统计类demo.MathGame中 |
| primeFactors方法 | |
OGNL举例
调用静态函数
ognl '@java.lang.System@out.println("hello")'
获取静态类的静态字段
ognl '@demo.MathGame@random'
执行多行表达式,赋值给临时变量,返回一个List
ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
watch举例
作用:方法执行数据观测,让你能方便的观察到指定方法的调用情况。
watch 命令定义了4个观察事件点,即 -b 方法调用前, -e 方法异常后, -s 方法返回后, -f方法结束后
4个观察事件点 -b 、 -e 、 -s 默认关闭, -f 默认打开,当指定观察点被打开后,在相应事件点会 对观察表达式进行求值并输出
watch demo.MathGame primeFactors "{params,returnObj}" -x 2
观察demo.MathGame类中primeFactors方法出参和返回值,结果属性遍历深度为2。 params表示所有参数数组,returnObject表示返回值
watch demo.MathGame primeFactors "{params,returnObj,throwExp}" -x 2 -b
观察方法入参,对比前一个例子,返回值为空(事件点为方法执行前,因此获取不到返回值),可以是单引号的,throwExp表示抛出的异常
同时观察方法调用前和方法返回后,参数里-n 2,表示只执行两次。 这里输出结果中,第一次输出的是方法调用前的观察表达式的结果,第二次输出的是方法返回后的表达式的结果
params表示参数,target表示执行方法的对象,returnObject表示返回值
watch demo.MathGame primeFactors "{params,target,returnObj}" -x 2 -b -s -n 2
使用target.field_name访问当前对象的某个属性
watch demo.MathGame primeFactors 'target.illegalArgumentCount'
条件表达式的例子,输出第1参数小于的情况
watch demo.MathGame primeFactors "{params[0],target}" "params[0]<0"
trace举例
作用: 方法内部调用路径,并输出方法路径上的每个节点上耗时
watch/stack/trace这个三个命令都支持 #cost
trace函数指定类的指定方法
trace demo.MathGame run
如果方法调用的次数很多,那么可以用-n参数指定捕捉结果的次数。比如下面的例子里,捕捉到一次调用 就退出命令。
trace demo.MathGame run -n 1
默认情况下,trace不会包含jdk里的函数调用,如果希望trace jdk里的函数,需要显式设置-- skipJDKMethod false。
trace --skipJDKMethod false demo.MathGame run
据调用耗时过滤,trace大于0.5ms的调用路径
trace demo.MathGame run '#cost > .5'
stack举例
作用: 输出当前方法被调用的调用路径
watch/stack/trace这个三个命令都支持 #cost
获取primeFactors的调用路径
stack demo.MathGame primeFactors
条件表达式来过滤,第0个参数的值小于0,-n表示获取2次
stack demo.MathGame primeFactors 'params[0]<0' -n 2
据执行时间来过滤,耗时大于5毫秒
stack demo.MathGame primeFactors '#cost>5'
sc举例
sc:search class, 查看 JVM 已加载的类信息
sc -df com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider
-d : detail
-f : field
这个信息重要哈,以后经常用 **classLoaderHash** 1772a47b
4. arthas使用遇到的问题
- 启动粘附的时候遇到下图error
[ERROR] The telnet port 3658 is used by process 979 instead of target process 70476, you will connect to an unexpected process.
cause:
979的pid占用了3658的arthas连接端口,上一次选择进程进行连接没有正常退出,arthas会保存
上一次监听进程,导致本次选择新进程进行连接时,与监听中记录的进程id不同,结果出现错误
solution:
再次进入上次粘附的pid,stop命令即可
学习一些常见的案例有助于我们更好的理解arthas
获取propeties文件的变量的值,排查配置的优先级
1、 ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getEnvironment().getProperty("custom.name")' -c 1772a47b
2、 第二张图也可以获取所有的环境变量
获取静态的N种方式
1. getstatic com.wangji92.arthas.plugin.demo.controller.StaticTest INVOKE_STATIC_NAME -x 3
2. @class@method(args) @class@field 这样的方式获取静态方法和静态字段
ognl -x 3 '@com.wangji92.arthas.plugin.demo.controller.StaticTest@INVOKE_STATIC_NAME' -c 1772a47b
3. ognl -x 3 '@com.wangji92.arthas.plugin.demo.controller.StaticTest@getInvokeStaticName()' -c 1772a47b
4. ognl -x 3 '#field=@com.wangji92.arthas.plugin.demo.controller.StaticTest@class.getDeclaredField("INVOKE_STATIC_NAME"),#field.setAccessible(true),#field.set(null,"dyw ")' -c 1772a47b
]
获取map其中的一个key对应的value
getstatic com.wangji92.arthas.plugin.demo.controller.CommonController userMap 'entrySet().iterator.{? #this.key=="roy"}' -x 3
- arthas平常常用的命令总结
1.trace com.roy.controller.CommonController traceException -n 5 --skipJDKMethod false > roynight.txt &
只要带了条件表达式就可以加 -v来显示条件表达式是否满足,一般trace ,stack,watch都可以加条件表达式,异步执行这个任务
jobs
pwd
2. 获取静态变量的值
第一种办法:
public static final String STATIC_VAL = "roy";
针对这个静态变量按照图1:ognl get static method field获取图2
先填充classloader,再获取如下的ognl语句
ognl -x 3 '@com.roy.controller.CommonController@STATIC_VAL' -c 18b4aac2
第二种办法
按照图3的方法simple get static field
可以获取这个命令执行
getstatic com.roy.controller.CommonController STATIC_VAL -x 3
4. 如何获取配置变量的值
首先配置你的spring容器,如图4
然后再按照图4下面的图3选择 get selected Spring Property 获取命令
ognl -x 3 '#springContext=@com.roy.config.ApplicationContextManager@context,#springContext.getEnvironment().getProperty("server.port")' -c 18b4aac2
5. OGNL语法糖
@class@method(args)和@class@field
如何获取一个静态变量的值
simple get static field , ognl get static method field
如何修改一个静态变量
private static final String INVOKE_STATIC_FINAL = "INVOKE_STATIC_FINAL";
针对这个静态变量ognl modify static field
ognl -x 3 '#field=@com.roy.service.StaticService@class.getDeclaredField("INVOKE_STATIC_FINAL"),#modifiers=#field.getClass().getDeclaredField("modifiers"),#modifiers.setAccessible(true),#modifiers.setInt(#field,#field.getModifiers() & ~@java.lang.reflect.Modifier@FINAL),#field.setAccessible(true),#field.set(null," ")'
修改最后一个值即可
public static String getInvokeStaticName() {
return INVOKE_STATIC_NAME;
}
针对这个方法图6获取取材
static ognl Grammar Help watch static Content
watch com.roy.controller.CommonController getRandomInteger '{params,returnObj,throwExp,@com.roy.service.StaticService@getInvokeStaticName()}' -n 5 -x 3
我们可以在watch里面随意加一些方法,从而在调用的时候得到执行
6. 在线修改logger
如图7可以修改logger的级别
logger --name com.roy.service.LoggerDemo
logger --name com.roy.service.LoggerDemo --level trace -c 18b4aac2
6. arthas基础命令
6.1 tt命令
time-tunnel 时间隧道
tt -l, 对现有记录进行检索。显示所有已经记录的列表
tt -t demo.MathGame primeFactors, 记录下当前方法的每次调用环境现场
tt -s 'method.name=="primeFactors"', 需要筛选出 primeFactors 方法的调用信息
tt -i 1002, 通过 -i 参数后边跟着对应的 INDEX 编号查看到他的详细信息
tt -t com.wangji92.arthas.plugin.demo.controller.CommonController userFastJson -n 5 记录下当前方法的每次调用环境现场。
-n 参数指定你需要记录的次数
通过 -m 参数指定 Class 匹配的最大数量,防止匹配到的 Class 数量太多导致 JVM 挂起,默认值是 50
解决方法重载
tt -t *Test print params.length==1
tt -t *Test print 'params[1] instanceof Integer'
tt -t *Test print params[0].mobile=="13989838402"
tt -t demo.MathGame primeFactors
tt -t -m 1 demo.MathGame primeFactors
tt 命令有很多个主参数,-t 就是其中之一。这个参数的表明希望记录下类 *Test 的 print 方法的每次执行情况。
-n 参数指定你需要记录的次数,当达到记录次数时 Arthas 会主动中断 tt 命令的记录过程,避免人工操作无法停止的情况。 -m 1,通过 -m 参数指定 Class 匹配的最大数量,防止匹配到的 Class 数量太多导致 JVM 挂起,默认值是 50。
过滤 ,tt -s 'method.name=="primeFactors"'
tt -i 1003, 对于具体一个时间片的信息而言,你可以通过 -i 参数后边跟着对应的 INDEX 编号查看到他的详细信息。
-p:重做一次调用
tt 获取容器对象以后做一些操作
-w, --watch-express 观察时空隧道使用ognl 表达式
tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod
tt -i index -w 'target.getApplicationContext()'
tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'
6.1 profiler
profiler start,启动profiler,默认情况下,生成的是cpu的火焰图,即event为cpu。可以用--event参数来指定。
profiler list,### 显示支持的事件
profiler getSamples,### 获取已采集的sample的数量
profiler status, ### 查看profiler状态
profiler stop, ### 停止profiler
profiler stop --file /tmp/output.svg,默认情况下,生成的结果保存到应用的工作目录下的arthas-output目录。可以通过 --file参数来指定输出结果路径。
profiler stop --format html,默认情况下,结果文件是svg格式,如果想生成html格式,可以用--format参数指定:
如何看火焰图
y轴表示调用栈,每一层都是一个函数,调用栈越深,火焰久越高,顶部就是正在执行的函数,下方都是他的父函数
X轴 表示抽样数,如果一个函数中X轴占据的宽度越宽,就表示他被抽到的次数多,就是执行的时间长,注意X轴不代表时间,而是所有的调用栈合并后,按字母排列的
火焰图就是看顶层的哪个函数占据的宽度最大,只要有平顶,就表示该函数可能存在性能问题
help
cat
grep -n -m -i -e
sysgrop | grep java -n -m10,显示前10行
thread | grep -e "o+"使用正则表达式,显示包含2个o字符的线程信息
sysprop | grep -e "\d{2,}" 包含两个数字以上
pwd
cls
session
reset
version
history
quit
stop
ctrl + c: 结束当前命令
ctrl + a: 回到行首
ctrl + c: 回到行尾
7. JVM相关的命令
dashboard:显示当前系统的实时数据面板,按q或ctrl+c退出
thread:查看当前 JVM 的线程堆栈信息,当没有参数时,显示所有线程的信息
thread 1:显示1号线程的运行堆栈
thread -b:找出当前阻塞其他线程的线程
thread -i 1000 -n 3: 指定采样时间间隔,每过1000毫秒采样,显示最占时间的3个线程
thread --state WAITING: 查看处于等待状态的线程
jvm:查看当前 JVM 的信息
sysenv:查看所有环境变量
sysenv USER:查看单个环境变量
vmoption: 查看,更新VM诊断相关的参数
vmoption PrintGCDetails: 查看指定的选项
vmoption PrintGCDetails true: 更新指定的选项
8. sc和sm
sc:sc -d demo.MathGame,打印类的详细信息
sc -df demo.MathGame, 打印出类的Field信息
sm java.lang.String
sm -d java.lang.String toString
9.jad、mc、redefine
jad:反编译指定已加载类的源码jad --source-only demo.MathGame
jad demo.MathGame main,反编译指定的函数
mc:Memory Compiler/内存编译器,编译 .java 文件生成 .class
mc /root/Hello.java, 在内存中编译Hello.java为Hello.class
mc -d /root/bbb /root/Hello.java,可以通过-d命令指定输出目录
redefine:加载外部的 .class 文件,redefine到JVM里
1. 使用jad反编译demo.MathGame输出到/root/MathGame.java
jad --source-only demo.MathGame > /root/MathGame.java
2.按上面的代码编辑完毕以后,使用mc内存中对新的代码编译
mc /root/MathGame.java -d /root
3.使用redefine命令加载新的字节码
redefine /root/demo/MathGame.class
dump:将已加载类的字节码文件保存到特定目录:logs/arthas/classdump/
dump java.lang.String:把String类的字节码文件保存到~/logs/arthas/classdump/目录下
dump demo.*, 把demo包下所有的类的字节码文件保存到~/logs/arthas/classdump/目录下
classloader:按类加载类型查看统计信息
classloader -l:按类加载实例查看统计信息,可以看到类加载的hashCode
classloader -t:查看ClassLoader的继承树
classloader -c 680f2737: 通过类加载器的hash,查看此类加载器实际所在的位置
classloader -c 680f2737 -r META-INF/MANIFEST.MF: 使用ClassLoader去查找指定资源resource所在的位置
classloader -c 680f2737 -r java/lang/String.class: 使用ClassLoader去查找类的class文件所在的位置
classloader -c 70dea4e --load java.lang.String: 使用ClassLoader去加载类
10.monitor
monitor -c 5 demo.MathGame primeFactors: 过5秒统计一次,统计类demo.MathGame中primeFactors方法
11.watch
target
params表示参数,target表示执行方法的对象,returnObject表示返回值
watch demo.MathGame primeFactors 'target'
watch demo.MathGame primeFactors 'target.illegalArgumentCount',使用target.field_name访问当前对象的某个属性
12. trace,方法内部调用路径,并输出方法路径上的每个节点上耗时
观察表达式的构成主要由ognl 表达式组成,所以你可以这样写 "{params,returnObj}" ,只要是 一个合法的 ognl 表达式,都能被正常支持。
watch/stack/trace这个三个命令都支持 #cost
trace -E com.test.ClassA|org.test.ClassB method1|method2|method3,可以用正则表匹配路径上的多个类和函数,一定程度上达到多层trace的效果。
13. stack
stack demo.MathGame primeFactors 'params[0]<0' -n 2, 条件表达式来过滤,第0个参数的值小于0,-n表示获取2次
stack demo.MathGame primeFactors '#cost>5', 据执行时间来过滤,耗时大于5毫秒
7. 一些优秀的issue
去选择案例的时候 选择用户的user-case