Arthas
一、Arthas简介
Arthas 官网:https://arthas.aliyun.com/doc/install-detail.html
Arthas-idea-plugin 插件文档:https://www.yuque.com/arthas-idea-plugin/help/pzldzl
Arthas 用户案例:https://github.com/alibaba/arthas/issues?q=label%3Auser-case
二、Arthas火焰图
1.Async-profiler
Async-profiler 说明:https://www.yuque.com/arthas-idea-plugin/help/iisg20
2.启动耗时火焰图
(1)在Idea的Springboot启动主类 VM Options 上添加如下代码:
在应用启动的JVM参数中,添加debugger参数,注意其中的suspend参数需要设为y,表示在debugger连接之前,程序会进行阻塞等待。
-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=10000
(2)新建一个shell窗口启动Arthas
anubis:arthas-packaging-3.7.1-bin/ $ ./as.sh [10:05:06]
Arthas script version: 3.7.1
[INFO] JAVA_HOME: /Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home
Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 43488
[2]: 14570
[3]: 44779 org.jetbrains.jps.cmdline.Launcher
[4]: 44780 -- main class information unavailable
选择第四个,启动profiler,由于我们需要分析启动过程的耗时阶段,所以我们需要指定跟踪采集的事件为wall
profiler -e wall start
(3)新建一个shell窗口触发程序启动,
利用jdb(jdk自带的debugger工具)连接对应的JVM进程,将此应用run起来。
jdb -attach localhost:10000
连接完成后,执行cont命令,让程序运行起来
cont
(4)程序启动完成后在Arthas的Shell窗口内输入
profiler stop --file /Users/anubis/Downloads/software/arthas-packaging-3.7.1-bin/rcs_start_server.html
三、Arthas常用命令
1.trace
查看方法调用链路以及方法各部分的耗时(配合arthas插件使用)
# java.* 下的函数调用默认会忽略掉。通过增加--skipJDKMethod false参数可以打印出来
# -n 参数指定捕捉结果的次数
trace com.asiainfo.pay.check.analyzer.service.impl.CsvTxtAnalyzerServiceImpl columnMap -n 5 --skipJDKMethod false
# -E trace 多个类或者多个函数 '1==1' 表示全部 method1|method2|method3
trace -E com.asiainfo.pay.check.analyzer.service.impl.CsvTxtAnalyzerServiceImpl columnMap -n 5 --skipJDKMethod false '1==1'
2.jad
反编译类/方法
# jad + 类全路径
jad com.tracer.service.impl.CheckServiceImpl
# jad + 类全路径 + 空格 + 方法名
jad com.tracer.service.impl.CheckServiceImpl checkFileFun
3.retransform
加载外部的.class文件到jvm内
# 查看以及加载的类(显示Id)
retransform -l
# 删除指定加载的类
retransform -d <Id>
# 加载类到JVM(本地编译好后将class文件复制到主机上)
retransform /app/check/arthas-packaging-3.7.1-bin/CheckServiceImpl.class
- 注意 加载类到JVM,不允许新增加字段和方法。
4.vmoption
查看,更新 VM 诊断相关的参数
[arthas@56963]$ vmoption
KEY VALUE ORIGIN WRITEABLE
---------------------------------------------------------------------------------------------
HeapDumpBeforeFullGC false DEFAULT true [JVM准备Full GC之前,生成堆快照]
HeapDumpAfterFullGC false DEFAULT true [JVMFull GC之后,生成堆快照]
HeapDumpOnOutOfMemory false DEFAULT true [当JVM遇到内存溢出错误时,是否自动创建堆内存转储]
Error
HeapDumpPath DEFAULT true [堆快照文件保存路径]
CMSAbortablePrecleanW 100 DEFAULT true [在并发标记清除(CMS)收集器中,可中断预清理阶段等待用户线程完成的最长时]
aitMillis
CMSWaitDuration 2000 DEFAULT true [CMS收集器在执行某些操作前等待其他线程完成的最长时间(毫秒)]
CMSTriggerInterval -1 DEFAULT true [触发CMS垃圾回收的间隔时间(-1表示使用默认值,由JVM自行决定)]
PrintGC false DEFAULT true [控制是否打印GC事件的基本信息]
PrintGCDetails true MANAGEMENT true [打印GC的详细信息,包括各代的内存变化等]
PrintGCDateStamps false DEFAULT true [在GC日志中包含日期时间戳]
PrintGCTimeStamps false DEFAULT true [包含GC发生的时间戳(以毫秒为单位自JVM启动)]
PrintGCID false DEFAULT true [是否在日志中包含GC事件的唯一标识符]
PrintClassHistogramBe false DEFAULT true [在GC前或特定时刻是否打印类的直方图(即内存占用情况)]
foreFullGC
PrintClassHistogramAf false DEFAULT true [在GC后或特定时刻是否打印类的直方图(即内存占用情况)]
terFullGC
PrintClassHistogram false DEFAULT true [在特定时刻是否打印类的直方图(即内存占用情况)]
MinHeapFreeRatio 0 DEFAULT true [控制堆内存的最小空闲比例]
MaxHeapFreeRatio 100 DEFAULT true [控制堆内存的最大空闲比例]
PrintConcurrentLocks false DEFAULT true [在进行垃圾回收时,是否打印持有锁的线程信息,帮助诊断死锁问题]
Arthas:
监控:
# 请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,
# 因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行 stop 或将增强过的类执行 reset 命令。
# 1、quit 退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
# 2、shutdown——关闭 Arthas 服务端,所有 Arthas 客户端全部退出
# 3、stop——和shutdown命令一致
monitor——方法执行监控
watch——方法执行数据观测
trace——方法内部调用路径,并输出方法路径上的每个节点上耗时
stack——输出当前方法被调用的调用路径
tt——方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
watch:
让你能方便的观察到指定方法的调用情况。
能观察到的范围为:返回值、抛出异常、入参,通过编写 OGNL 表达式进行对应变量的查看。
参数说明:
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
express 观察表达式
condition-express 条件表达式
-b 在方法调用之前观察
-e 在方法异常之后观察
-s 在方法返回之后观察
-f 在方法结束之后(正常返回和异常返回)观察 默认开启
-E 开启正则表达式匹配,默认为通配符匹配
-x 指定输出结果的属性遍历深度,默认为 1
-n 执行的次数
特别说明:
watch 命令定义了4个观察事件点,
即 -b 方法调用前,-e 方法异常后,-s 方法返回后,-f 方法结束后
4个观察事件点 -b、-e、-s 默认关闭,-f 默认打开,
当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出
这里要注意方法入参和方法出参的区别,有可能在中间被修改导致前后不一致,
除了 -b 事件点 params 代表方法入参外,其余事件都代表方法出参
当使用 -b 时,由于观察事件点是在方法调用前,此时返回值或异常均不存在
-x 代表输出结果的深度 ,默认为 1
使用参考:
常规用法:
watch class method {params,returnObj,throwExp} -x 1 '#cost>200'
观察表达式 { } 可以包裹结果集 , 分割。输出的表达式变量定义
params 代表参数数组,returnObj 代表返回值,throwExp 代表抛出的异常。
过滤耗时大于200ms的
特殊用法:
1、调用第一个参数的方法或属性
watch class method "{params[0].length()}" -x 1
如果是列表参数,则可以使用这种方式来获取列表中每个对象的指定属性
watch class method "{params[0].{ #this.length()}}" -x 1
2、按照条件过滤
如果是列表,则会过滤列表中长度大于7的字符串
watch class method "{params[0].{? #this.length() > 7}}" -x 1
watch class method "{params,returnObj,throwExp}" "params[0].length() > 7" -x 1
字符串长度 > 7 才会输出 params[0] 的值
3、过滤后统计
watch class method "{params[0].{? #this.length() > 9}.size()}" -x 1
trace:
trace 命令能主动搜索 class-pattern/method-pattern 对应的方法内部调用路径,
渲染和统计整个调用链路上的所有性能开销和追踪调用链路。
参数说明:
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式
-E 开启正则表达式匹配,默认为通配符匹配
-n 命令执行次数
#cost 方法执行耗时
使用参考:
常规用法:
trace class method '#cost>100' -n 1
过滤大于100ms的调用链,只输出一次
特殊用法:
trace命令只会trace匹配到的函数里的子调用,并不会向下trace多层。
因为trace是代价比较贵的,多层trace可能会导致最终要trace的类和函数非常多。
动态trace:
3.3.0 版本后支持。
打开终端1,trace run函数,可以看到打印出 listenerId: 1:
[arthas@59161]$ trace demo.MathGame run
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 112 ms, listenerId: 1
`---ts=2020-07-09 16:48:11;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
`---[1.389634ms] demo.MathGame:run()
`---[0.123934ms] demo.MathGame:primeFactors() #24 [throws Exception]
现在想要深入子函数primeFactors,可以打开一个新终端2,
使用telnet localhost 3658连接上arthas,
再trace primeFactors时,指定listenerId。
再查看终端1,可以发现trace的结果增加了一层,打印了primeFactors函数里的内容
`---ts=2020-07-09 16:49:29;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
`---[0.492551ms] demo.MathGame:run()
`---[0.113929ms] demo.MathGame:primeFactors() #24 [throws Exception]
`---[0.061462ms] demo.MathGame:primeFactors()
`---[0.001018ms] throw:java.lang.IllegalArgumentException() #46
通过指定listenerId的方式动态trace,可以不断深入。
另外 watch/tt/monitor等命令也支持类似的功能。