简介
Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。
背景
通常,本地开发环境无法访问生产环境。如果在生产环境中遇到问题,则无法使用 IDE 远程调试。更糟糕的是,在生产环境中调试是不可接受的,因为它会暂停所有线程,导致服务暂停。
开发人员可以尝试在测试环境或者预发环境中复现生产环境中的问题。但是,某些问题无法在不同的环境中轻松复现,甚至在重新启动后就消失了。
如果您正在考虑在代码中添加一些日志以帮助解决问题,您将必须经历以下阶段:测试、预发,然后生产。这种方法效率低下,更糟糕的是,该问题可能无法解决,因为一旦 JVM 重新启动,它可能无法复现,如上文所述。
Arthas 旨在解决这些问题。开发人员可以在线解决生产问题。无需 JVM 重启,无需代码更改。 Arthas 作为观察者永远不会暂停正在运行的线程。
技术栈
Instrumentation:
- 方法追踪:Instrumentation 技术允许 Arthas 追踪方法的执行情况,包括方法的调用次数、执行时间等。
- 方法修改与增强:可以动态地修改方法体,用于实现一些诊断或监控功能。
Java Agent:
- 应用程序注入:Arthas 使用 Java Agent 技术将自身注入到目标应用程序的 JVM 中,从而可以实时监控和诊断应用程序运行状态。
Attach API:
- 与运行中的 JVM 交互:Arthas 使用 Attach API 与运行中的 JVM 通信,获取应用程序运行时的信息,比如线程信息、堆栈信息等。
ASM(字节码操作库):
- 字节码分析与修改:ASM 技术用于分析和修改 Java 字节码,Arthas 利用 ASM 实现对类和方法的动态增强,比如动态地添加监控、拦截器等。
反射:
- 动态操作类与方法:反射机制用于动态地检索类信息、调用方法和操作字段,Arthas 利用反射来获取和修改应用程序的运行时信息。
OGNL(Object-Graph Navigation Language):
- 数据查询和操作:OGNL 用于处理对象图的查询和操作,Arthas 可能使用 OGNL 实现对运行时数据的查询和分析,例如查看对象状态等。
Java Debug Wire Protocol (JDWP) 和 Java Virtual Machine Tool Interface (JVMTI):
- JVM调试工具:通过这些调试接口,Arthas 能够与 JVM 通信,收集应用程序的运行状态和信息,实现了对线程、堆栈、内存等方面的监控和诊断功能。。
下载
从maven仓库下载
最新版本:https://arthas.aliyun.com/download/latest_version?mirror=aliyun
从Github Releases页下载
https://github.com/alibaba/arthas/releases
启动
- 在命令行下面执行(使用和目标进程一致的用户启动,否则可能 attach 失败):
java -jar arthas-boot.jar
- 选择应用 java 进程:
$ $ java -jar arthas-boot.jar *
[1]: 35542
[2]: 71560 math-game.jar
注释:可以通过ps -ef | grep “xxx” 命令查看需要跟踪进程的进程号
命令列表
jvm 相关
- dashboard - 当前系统的实时数据面板
- getstatic - 查看类的静态属性
- heapdump - dump java heap, 类似 jmap 命令的 heap dump 功能
- jvm - 查看当前 JVM 的信息
- logger - 查看和修改 logger
- mbean - 查看 Mbean 的信息
- memory - 查看 JVM 的内存信息
- ognl - 执行 ognl 表达式
- perfcounter - 查看当前 JVM 的 Perf Counter 信息
- sysenv - 查看 JVM 的环境变量
- sysprop - 查看和修改 JVM 的系统属性
- thread - 查看当前 JVM 的线程堆栈信息
- vmoption - 查看和修改 JVM 里诊断相关的 option
- vmtool - 从 jvm 里查询对象,执行 forceGc
class/classloader 相关
- classloader - 查看 classloader 的继承树,urls,类加载信息,使用 classloader 去 getResource
- dump - dump 已加载类的 byte code 到特定目录
- jad - 反编译指定已加载类的源码
- mc - 内存编译器,内存编译.java文件为.class文件
- redefine - 加载外部的.class文件,redefine 到 JVM 里
- retransform - 加载外部的.class文件,retransform 到 JVM 里
- sc - 查看 JVM 已加载的类信息
- sm - 查看已加载类的方法信息
monitor/watch/trace 相关
注意
请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行 stop 或将增强过的类执行 reset 命令。
- monitor - 方法执行监控
- stack - 输出当前方法被调用的调用路径
- trace - 方法内部调用路径,并输出方法路径上的每个节点上耗时
- tt - 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
- watch - 方法执行数据观测
profiler/火焰图
- profiler - 使用async-profiler在新窗口打开对应用采样,生成火焰图
- jfr - 动态开启关闭 JFR 记录
常用命令详细说明
trace
race 命令能主动搜索 class-pattern/method-pattern 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。
参数说明
参数名称 | 参数说明 |
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
[E] | 开启正则表达式匹配,默认为通配符匹配 |
[n:] | 命令执行次数 |
#cost | 方法执行耗时 |
[m <arg>] | 指定 Class 最大匹配数量,默认值为 50。长格式为[maxMatch <arg>]。 |
注意事项
- trace 能方便的帮助你定位和发现因 RT 高而导致的性能问题缺陷,但其每次只能跟踪一级方法的调用链路。
- 3.3.0 版本后,可以使用动态 Trace 功能,不断增加新的匹配类,参考下面的示例。
- 目前不支持 trace java.lang.Thread getName,参考 issue: #1610 ,考虑到不是非常必要场景,且修复有一定难度,因此当前暂不修复
使用参考
trace 函数
$ trace demo.MathGame run //trace 完整的类路径 方法或函数名称
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 28 ms.
`---ts=2019-12-04 00:45:08;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
`---[0.617465ms] demo.MathGame:run()
`---[0.078946ms] demo.MathGame:primeFactors() #24 [throws Exception]
`---ts=2019-12-04 00:45:09;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
`---[1.276874ms] demo.MathGame:run()
`---[0.03752ms] demo.MathGame:primeFactors() #24 [throws Exception]
trace 次数限制
如果方法调用的次数很多,那么可以用-n参数指定捕捉结果的次数。比如下面的例子里,捕捉到一次调用就退出命令
$ trace demo.MathGame run -n 1
包含 jdk 的函数
- --skipJDKMethod <value> skip jdk method trace, default value true.
默认情况下,trace 不会包含 jdk 里的函数调用,如果希望 trace jdk 里的函数,需要显式设置--skipJDKMethod false。
$ trace --skipJDKMethod false demo.MathGame run
根据调用耗时过滤
$ trace demo.MathGame run '#cost > 10'
只会展示耗时大于 10ms 的调用路径,有助于在排查问题的时候,只关注异常情况
trace 多个类或者多个函数
trace 命令只会 trace 匹配到的函数里的子调用,并不会向下 trace 多层。因为 trace 是代价比较贵的,多层 trace 可能会导致最终要 trace 的类和函数非常多。
可以用正则表匹配路径上的多个类和函数,一定程度上达到多层 trace 的效果。
trace -E com.test.ClassA|org.test.ClassB method1|method2|method3
排除掉指定的类
使用 --exclude-class-pattern 参数可以排除掉指定的类,比如:
trace javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter
Profiler(火焰图)
profiler 命令支持生成应用热点的火焰图。本质上是通过不断的采样,然后把收集到的采样结果生成火焰图。
profiler 命令基本运行结构是 profiler action [actionArg]
参数说明
参数名称 | 参数说明 |
action | 要执行的操作 |
actionArg | 属性名模式 |
[i:] | 采样间隔(单位:ns)(默认值:10'000'000,即 10 ms) |
[f:] | 将输出转储到指定路径 |
[d:] | 运行评测指定秒 |
[e:] | 要跟踪哪个事件(cpu, alloc, lock, cache-misses 等),默认是 cpu |
启动 profiler
$ profiler start
Started [cpu] profiling
获取已采集的 sample 的数量
$ profiler getSamples
23
查看 profiler 状态
$ profiler status
[cpu] profiling is running for 4 seconds
可以查看当前 profiler 在采样哪种event和采样时间。
停止 profiler
生成 html 格式结果
默认情况下,结果文件是html格式,也可以用--format参数指定:
$ profiler stop --format html
profiler output file: /tmp/test/arthas-output/20211207-111550.html
OK
通过浏览器查看 arthas-output 下面的 profiler 结果
默认情况下,arthas 使用 3658 端口,则可以打开: http://localhost:3658/arthas-output/ 查看到arthas-output目录下面的 profiler 结果:
如何查看火焰图
关于火焰图怎么看,一言以蔽之:火焰图里,横条越长,代表使用的越多,从下到上是调用堆栈信息。
- 绿色部分代表Java代码
- 黄色部分代表JVM C++代码
- 橙色部分代表内核态C语言代码
- 红色代表用户态C语言代码