arthas学习使用

612 阅读11分钟

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代表指定主类启动

image.png

image.png

image.png

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方法出参和返回值,结果属性遍历深度为2params表示所有参数数组,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命令即可

image.png

学习一些常见的案例有助于我们更好的理解arthas

获取propeties文件的变量的值,排查配置的优先级

   1ognl -x 3 '#springContext=@com.wangji92.arthas.plugin.demo.common.ApplicationContextProvider@context,#springContext.getEnvironment().getProperty("custom.name")' -c 1772a47b
   2、 第二张图也可以获取所有的环境变量

image.png

image.png

获取静态的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
    
    
    
    


image.png

image.png

image.png

image.png

image.png]

获取map其中的一个key对应的value

getstatic com.wangji92.arthas.plugin.demo.controller.CommonController userMap 'entrySet().iterator.{? #this.key=="roy"}' -x 3

  1. 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



image.png

image.png

image.png

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

image.png

image.png

5. OGNL语法糖
@class@methodargs)和@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里面随意加一些方法,从而在调用的时候得到执行


image.png

image.png

6. 在线修改logger
如图7可以修改logger的级别
logger --name com.roy.service.LoggerDemo
logger --name com.roy.service.LoggerDemo --level trace -c 18b4aac2

image.png

image.png

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

image.png

github.com/alibaba/art…

github.com/alibaba/art…

github.com/alibaba/art…

github.com/alibaba/art…

8. 学习资料

www.bilibili.com/video/BV19k…