本文主要是介绍Arthas(阿尔萨斯)定位线上问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Arthas 阿尔萨斯使用
- 参考
- 实际使用案例
- 线上安装与挂载
- 火焰图
- 查找类、查看类详细信息与反编译代码
- 查看类中的所有field值
- thread的各种操作
- 查看入参、返回值、异常
- 线上修改代码:jad/mc/redefine/retransform
- 性能监控
- dump
- 找到异常线程并查看堆栈
- Mbean
- 案例总结
- trace查看耗时进行调优
- Arthas底层实现
- WeakHashMap死循环引起CPU跑满
- JDK函数调用查询与后台脚本打印数据
- ognl表达式筛选并观察输入输出
- 查看某个Spring引用的具体实现
- tt获取Spring容器内的bean
- list出现环导致死循环引发的CPU负载过高
- 奇怪日志来源定位
参考
官方文档:https://arthas.aliyun.com/doc/
用户案例:https://github.com/alibaba/arthas/issues?q=label%3Auser-case
一图流:https://github.com/alibaba/arthas/issues/1003
IDE插件官方文档:https://www.yuque.com/arthas-idea-plugin/help/pe6i45
一篇很好的总结:https://blog.csdn.net/u013735734/article/details/102930307?utm_source=app&app_version=4.13.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
JVM工具专栏:https://cloud.tencent.com/developer/column/3195/tag-10688
https://www.cnblogs.com/chiangchou/p/jvm-4.html
实际使用案例
基础命令:https://arthas.aliyun.com/doc/advanced-use.html#id2
线上安装与挂载
wget https://alibaba.github.io/arthas/arthas-boot.jar
jps -ml
ps -ef | grep java
java -jar arthas-boot.jar {pid}
火焰图
参考
火焰图基本科普与讲解: https://www.ruanyifeng.com/blog/2017/09/flame-graph.html
定位不同问题的不同类型火焰图 + 安装使用教程:<https://www.infoq.cn/article/a8kmnxdhbwmzxzsytlga >
本机可以随意安装火焰图生成工具。用于线上环境时,需要看线上环境用的火焰图生成工具是什么,生成图示之后传到本机进行分析即可。
profiler start
# 一段时间(>30s)后
profiler stop
exit
wget -O /usr/local/bin/toshelper http://tosv.byted.org/obj/ee-oi-intranet/toshelper && chmod +x /usr/local/bin/toshelper
toshelper /tmp/XXX.svg
查找类、查看类详细信息与反编译代码
参考
# 可以找到需要的类全路径
# -d 输出当前类的详细信息:所加载的原始文件来源、类的声明、
# 加载的ClassLoader(可以看出是哪个组件加载的)等详细信息。
sc -d *DispatcharServlet # 查看某个方法的信息,如果存在的话 ,不加方法的话查看所有方法
sm org.springframework.web.servlet.DispatcherServlet getHandler # 直接反编译出java 源代码,包含类加载器等额外信息
jad package.XXXService.method
sc -d查看详细类的详细信息
查看类中的所有field值
sc -d -f class
thread的各种操作
https://blog.csdn.net/u013735734/article/details/102930307?utm_source=app&app_version=4.13.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
4.2节
查看topn的线程 + 具体线程堆栈 + 查看blocking线程、死锁线程 + 查看线程池等实例。
https://arthas.aliyun.com/doc/thread.html
// 查看所有线程信息
thread
// 查看前N线程
thread -n 3// 查看具体线程
thread id
// 找出当前阻塞其他线程的线程
thread -b
// 查看指定状态的线程
thread --state WAITING
查看入参、返回值、异常
https://arthas.aliyun.com/doc/watch.html
支持复杂的设定
# 同时监控入参,返回值,及异常。如果有异常,直接打印出来
watch package.testMethod "{params, returnObj, throwExp}" -e -x 2
几个watch结合ognl的实例
# 观察CommonTest的test方法
# 输出 入参、返回结果、抛出的异常 —— 输出的内容可以动态调整
# 后面跟着的是 条件表达式,表示耗时超过10ms才输出
# -n 表示只执行一次,-x表示 入参和返回结果的展开层次为5层
watch *.CommonTest test "{params,returnObj,throwExp}" '#cost>10' -x 5 -n 1# 耗时大于10ms并且第一个参数等于1才输出
watch *.CommonTest test "{params,returnObj,throwExp}" '#cost>10 && params[0]==1' -x 5 -n 1# 第一个参数大于1 并且第二个参数等于hello才输出
watch *.CommonTest test "{params,returnObj,throwExp}" 'params[0]>1 && params[1]=="hello"' -x 5 -n 1
# 第一个参数小于5或者第二个参数等于"world"就输出
watch *.CommonTest test "{params,returnObj,throwExp}" 'params[0]<5 || params[1]=="wolrd"' -x 5 -n 1
# 第一个参数的name字段等于world时才输出。
# 由于在方法执行过程中参数的name属性可能发生改变,因此加上-b才能观察到真正的入参
watch -b *.CommonTest test "{params,returnObj,throwExp}" 'params[0].name=="wolrd"' -x 5 -n 1# 由于同时指定了-s和-b,所以方法被调用一次,就会输出2次结果(两个场景分开输出),分别是方法被调用前,和返回之后
# 注意,这里如果-n只设置成1,那么只会输出-b对应的输出,-s对应的输出由于没有次数了就无法输出了
watch *.CommonTest test '{params,returnObj,throwExp}' -x 5 -n 2 -s -b
线上修改代码:jad/mc/redefine/retransform
整体而言,还是使用Spring提供的hot swap更为合适。
https://github.com/alibaba/arthas/issues/537
Arthas: https://github.com/alibaba/arthas
jad命令:https://alibaba.github.io/arthas/jad.html
mc命令:https://alibaba.github.io/arthas/mc.html
redefine命令:https://alibaba.github.io/arthas/redefine.html
retransform:https://arthas.aliyun.com/doc/retransform.html
# 先反编译出class源码
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java # 然后使用外部工具编辑内容
vim /tmp/UserController.java# 查找加载UserController的ClassLoader
sc -d *UserController | grep classLoaderHash#output : classLoaderHash 1be6f5c3# 再编译成class,只需指明目录即可# 有时候mc命令不一定成功,尤其是很复杂的类有着各种注入各种依赖。
mc -c 1be6f5c3 /tmp/UserController.java -d /tmp # 最后,重新载入定义的类
redefine /tmp/com/example/demo/arthas/user/UserController.class
retransform /tmp/Test.class
更好的做法:
直接在IDE上完成class的编辑,然后去生产环境中将代码拷贝进去,运行redefine命令即可。
https://blog.csdn.net/javageektech/article/details/101443191
注意:
- redefine的class不能修改、添加、删除类的field和method,包括方法参数、方法名称及返回值
- 如果mc失败,可以在本地开发环境编译好class文件,上传到目标系统,使用redefine热加载class
- 目前redefine 和watch/trace/jad/tt等命令冲突,
- redefine后的原来的类不能恢复,redefine有可能失败
retransform直接替换jvm里已经加载的class方法。好处是可以恢复。
上传 .class 文件到服务器的技巧
性能监控
monitor:方法执行结果监控,统计重要信息。https://alibaba.github.io/arthas/monitor.html
$ monitor -c 5 demo.MathGame primeFactors
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 94 ms.timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------2018-12-03 19:06:38 demo.MathGame primeFactors 5 1 4 1.15 80.00%timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------2018-12-03 19:06:43 demo.MathGame primeFactors 5 3 2 42.29 40.00%timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------2018-12-03 19:06:48 demo.MathGame primeFactors 5 3 2 67.92 40.00%timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------2018-12-03 19:06:53 demo.MathGame primeFactors 5 2 3 0.25 60.00%timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------2018-12-03 19:06:58 demo.MathGame primeFactors 1 1 0 0.45 0.00%timestamp class method total success fail avg-rt(ms) fail-rate
-----------------------------------------------------------------------------------------------2018-12-03 19:07:03 demo.MathGame primeFactors 2 2 0 3182.72 0.00%
tt:时空隧道,记录了过程中的快照,方便Arthas内部线程重新发起一次调用。https://arthas.aliyun.com/doc/tt.html
// 留下快照
$ tt -lINDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
-------------------------------------------------------------------------------------------------------------------------------------1000 2018-12-04 11:15:38 1.096236 false true 0x4b67cf4d MathGame primeFactors1001 2018-12-04 11:15:39 0.191848 false true 0x4b67cf4d MathGame primeFactors1002 2018-12-04 11:15:40 0.069523 false true 0x4b67cf4d MathGame primeFactors1003 2018-12-04 11:15:41 0.186073 false true 0x4b67cf4d MathGame primeFactors1004 2018-12-04 11:15:42 17.76437 true false 0x4b67cf4d MathGame primeFactors91005 2018-12-04 11:15:43 0.4776 false true 0x4b67cf4d MathGame primeFactors
Affect(row-cnt:6) cost in 4 ms.
// 详细信息查询
$ tt -i 1003INDEX 1003GMT-CREATE 2018-12-04 11:15:41COST(ms) 0.186073OBJECT 0x4b67cf4dCLASS demo.MathGameMETHOD primeFactorsIS-RETURN falseIS-EXCEPTION truePARAMETERS[0] @Integer[-564322413]THROW-EXCEPTION java.lang.IllegalArgumentException: number is: -564322413, need >= 2at demo.MathGame.primeFactors(MathGame.java:46)at demo.MathGame.run(MathGame.java:24)at demo.MathGame.main(MathGame.java:16)Affect(row-cnt:1) cost in 11 ms.
发起执行
[arthas@10718]$ tt -t demo.MathGame run -n 5
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------1000 2021-01-08 21:54:17 0.901091 true false 0x7699a589 MathGame run
[arthas@10718]$ tt -w '@demo.MathGame@random.nextInt(100)' -x 1 -i 1000
@Integer[46]
trace:单纯时间统计https://arthas.aliyun.com/doc/trace.html
# 输出方法内部调用路径,并输出方法路径上的每个节点上耗时,根据耗时进行过滤
trace demo.MathGame run '#cost > 10'
dump
dump:dump 已加载类的 bytecode 到特定目录. https://arthas.aliyun.com/doc/dump.html
heapdump:dump java heap, 类似jmap命令的heap dump功能。https://arthas.aliyun.com/doc/heapdump.html
heapdump --live /tmp/jvm.hprof# 之后使用tos回传
wget -O /usr/local/bin/toshelper http://tosv.byted.org/obj/ee-oi-intranet/toshelper && chmod +x /usr/local/bin/toshelper
toshelper /opt/tiger/multi-tenant-custom-entity/multi-tenant-custom-entity-cos-web/arthas-output/20210720-102201.svg
找到异常线程并查看堆栈
thread命令参看文章前半截
https://github.com/alibaba/arthas/issues/1202
方法一:
# 查找进程
top
# 查看进程线程
top -Hp 进程号
#或者也可以采用下面2种ps命令来查看繁忙的线程信息
ps -mp pid -o THREAD,tid,time
ps -Lfp pid
# 16进制线程号
printf "%x\n" 目标线程号
# jstack查看线程堆栈,jstack会打印出所有线程堆栈
jstack 进程号 | grep 线程ID方法二:
# show busy threads的方法:直接打印出异常线程堆栈
wget --no-check-certificate https://raw.github.com/oldratlee/useful-scripts/release-2.x/bin/show-busy-java-threads
chmod +x show-busy-java-threads./show-busy-java-threads方法三:
# Arthas方法,查找cpu占用量最高的线程
thread -n 10
# 查看堆栈
thread pid方法四:
# jstack 、top的文件也可以使用这个网站来分析
https://fastthread.io/ft-index.jsp
Mbean
命令介绍:https://arthas.aliyun.com/doc/mbean.html
什么是Mbean与jmx:
https://juejin.cn/post/6856949531003748365
https://www.liaoxuefeng.com/wiki/1252599548343744/1282385687609378
一种JVM运行时的监控框架。
案例总结
https://github.com/alibaba/arthas/issues?page=1&q=label%3Auser-case
trace查看耗时进行调优
https://github.com/alibaba/arthas/issues/1892
使用trace查看调用链路,从中分析耗时严重的函数。
Arthas底层实现
ByteKit解读:
https://github.com/alibaba/arthas/issues/1310
https://github.com/alibaba/arthas/issues/1311
WeakHashMap死循环引起CPU跑满
https://github.com/alibaba/arthas/issues/1709
thread + sc的应用
1,异常线程堆栈章节查到线程并且发现是weakHashMap的get方法出了问题
2,sc -d 从code-source中查看jar包,发现这个jar包里用到的是线程不安全版本的weakHashMap,遂破案。
JDK函数调用查询与后台脚本打印数据
案例:System.gc查找
https://github.com/alibaba/arthas/issues/20
stack:https://arthas.aliyun.com/doc/stack.html
实时观察版本:
# Arthas默认关闭了对JDK类的自带类的增强,需要通过options命令打开。
options unsafe true
# stack命令:输出当前方法被调用的调用路径,实时观察谁调用了java.lang.System#gc,等待触发
# 实际上是不带每一步调用时间信息的trace版本
stack java.lang.System gc
后台打印版本
# 编辑脚本
cat system_gc.as# 脚本内容
options unsafe true
# 务必注意这里的-n,表示这个命令只执行一次,然后退出。如果不指定,Arthas 2.0会一直卡在那里,永远不退出,Arthas 3.0默认执行100次才会退出。在写批处理脚本的时候,请务必加上-n参数。
stack java.lang.System gc -n 1# 后台执行脚本1
./as.sh -b -f system_gc.as 59863 > system_gc.out 2>&1 &
# 后台执行脚本2,这种方式能够避免session因为连接超时中断的同时导致该脚本中断
nohup ./as.sh -b -f system_gc.as 59863 > system_gc.out 2>&1 &
# 后代执行脚本3,使用screen避免脚本中断
sudo yum install -b current screen
screen
./as.sh -b -f system_gc.as 59863 > system_gc.out 2>&1
# 即便网络断掉也没有问题。过一段时间之后,要查看之前的结果。如果连接断掉了,只需要输入以下命令
screen -r#过段时间观察即可
cat system_gc.out
ognl表达式筛选并观察输入输出
ognl表达式:https://github.com/alibaba/arthas/issues/11
一些例子:https://github.com/alibaba/arthas/issues/71
# 打印线上某个容器的size
ognl '@xxx.common.redis.collections.UniqConcurrentSet@INSTANCE.dataIsNullSet.size()
# 在内部类某个方法中查看外部类容器大小
# https://github.com/alibaba/arthas/issues/772
watch sun.net.httpserver.ServerImpl$ServerTimerTask1 run -f "target.this$0.allConnections.size()
查看某个Spring引用的具体实现
日志打满问题解决:https://github.com/alibaba/arthas/issues/237
# -d显示详细信息
sc -d *LarkService*
# 查看具体实现类
getstatic -c 73ad2d6 io.netty.channel.nio.NioEventLoop logger 'getClass().getName()'
getstatic -c 73ad2d6 io.netty.channel.nio.NioEventLoop logger 'logger'
getstatic -c 73ad2d6 io.netty.channel.nio.NioEventLoop logger 'logger.getClass().getProtectionDomain().getCodeSource().getLocation()'
field: logger
# 查询其基类的积累的logger实现
getstatic -c 73ad2d6 io.netty.channel.nio.NioEventLoop logger 'logger.parent.parent.parent.parent.parent'
field: logger
tt获取Spring容器内的bean
https://github.com/alibaba/arthas/issues/482
时空隧道中能抓取到bean的调用
实际上用的就是idea插件生成的ognl语句。
list出现环导致死循环引发的CPU负载过高
https://github.com/alibaba/arthas/issues/1202
thread
thread pid# 查看方法调用情况
tt -t com.google.common.collect.HashBiMap seekByKey -n 100# SpringMVC应用,所有请求都会被RequestMappingHandlerAdapter拦截,我们通过tt命令,监听invokeHandlerMethod的执行,然后在页面随便点点,就会得到以下内容
[arthas@384]$ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod -n 10
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 622 ms.INDEX COST(ms) OBJECT CLASS METHOD
------------------------------------------------------------------------------------1000 481.203383 0x481eb705 RequestMappingHandlerAdapter invokeHandlerMethod 1001 3.432024 0x481eb705 RequestMappingHandlerAdapter invokeHandlerMethod
...
# tt 命令会记录方法调用时的所有入参和返回值、抛出的异常、对象本身等数据。通过 -i 参数后边跟着对应的 INDEX 编号查看这条记录的详细信息。再通过-w参数,指定一个OGNL表达式,查找相关对象
# Arthas会把当前执行的对象放到target变量中,通过target.getApplicationContext()就得到了SpringContext对象,这里同上一节内容
# OGNL写一个函数,来实现链表的环路检测,在OGNL里写一段环路检测代码里是不太容易的,一个bucket不太可能有50个以上的节点,所以就通过遍历次数是否大于50来判断是否有环路。
tt -i 1000 -w 'target.getApplicationContext().getBean("oaInfoManager").userCache.entrySet().{delegate}.{^ #loopCnt = 0,#foundCycle = :[ #this == null ? false : #loopCnt > 50 ? true : (#loopCnt = #loopCnt + 1, #foundCycle(#this.nextInKToVBucket))], #foundCycle(#this)}.get(0)' -x 2
奇怪日志来源定位
https://github.com/alibaba/arthas/issues/263
实际上是通过重写StringBuilder代码来定位某个奇怪日志的stack
使用到的技术:
redefine
这篇关于Arthas(阿尔萨斯)定位线上问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!