Arthas能做什么?
Arthas 是Alibaba开源的Java诊断工具,支持JDK 6+,Linux/Mac/Windows环境,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。
- 提供性能看板,包括线程、cpu、内存等信息,并且会定时的刷新。
- 根据各种条件查看线程快照,比如找出cpu占用率最高的n个线程等。
- 输出jvm的各种信息,如gc算法、gc次数及时间,jdk版本、classPath等
- 查看JVM的系统属性(System Property)和当前JVM的环境属性(System Environment Variables)
- 查看某个类的静态属性,也可以通过ognl表达式执行一些语句
- 查看已加载的类的详细信息,比如这个类从哪个jar包加载的,也可以查看类的方法的信息
- dump某个类的字节码到指定目录。
- 直接反编译指定的类。
- 查看类加载器的一些信息。
- 可以让jvm重新加载某个类。
- 监控方法的执行,同时可以获取到执行的入参、出参以及抛出的异常。
- 追踪方法执行的调用栈,以及各个方法的调用时间。
......
快速入门
在线下载
Bash curl -O https://arthas.aliyun.com/arthas-boot.jar |
运行程序
Bash java -jar arthas-boot.jar |
程序运行后,会显示出所有的java进程,在下面输入进程的序号Arthas就会attach到目标进程上,并输出日志,如下图ThreadBlockTest进程的编号是3,则输入3,再输入回车/enter即可 attach 到目标进程。
端口被占用问题处理:
java.exe -jar C:/Users/hongbo.liu/arthas-boot.jar --telnet-port 9991 --http-port -1
常用命令
基础命令
- help——查看命令帮助信息
- cat——打印文件内容,和linux里的cat命令类似
- echo–打印参数,和linux里的echo命令类似
- grep——匹配查找,和linux里的grep命令类似
- base64——base64编码转换,和linux里的base64命令类似
- tee——复制标准输入到标准输出和指定的文件,和linux里的tee命令类似
- pwd——返回当前的工作目录,和linux命令类似
- cls——清空当前屏幕区域
- session——查看当前会话的信息
- reset——重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
- version——输出当前目标 Java 进程所加载的 Arthas 版本号
- history——打印命令历史
- quit——退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
- stop——关闭 Arthas 服务端,所有 Arthas 客户端全部退出
- keymap——Arthas快捷键列表及自定义快捷键
Jvm相关
- dashboard——查看当前系统的实时数据面板
输入dashboard,按回车/enter,会展示当前进程的信息,按ctrl+c可以中断执行。
数据说明
|
JVM内部线程
|
- thread——查看当前 JVM 的线程堆栈信息 。
同 jdk 自带的jstack命令,java常见命令及Java Dump的使用传送门:https://www.cnblogs.com/kongzhongqijing/articles/5534624.html
参数说明
-n | 查看当前进程最忙碌的n个线程 |
-i | 指定采样时间,单元为毫秒,默认200ms |
-b | 阻塞其他线程的线程(进行死锁的排查) |
–state | 查看指定状态的线程 |
thread -n 3 -i 5000 : 列出5000ms内最忙的3个线程栈。(使用cpu时间最多的3个线程)
ps:本例中起三个线程分别执行死循环操作。
数据说明
|
thead -i 5000 查看到默认按cpu增量时间递减显示第一页线程信息。
线程的几种状态说明:
TIMED_WA为线程调用了指定的正的等待时间(为参数)的以下方法中的其一:
BLOCKED是指线程正在等待获取锁而阻塞。 WATTING是指线程调用wait()等方法主动让出cpu时间,等待其他线程唤醒(如:notify),被唤醒的线程可能继续向后执行(RUNNABLE),也可能需要再次进行锁竞争,进入BLOCKED状态。 |
thread -b 查看当前阻塞其他线程的线程。
可以看到测试demo中,多个线程抢占obj锁对象,其中有一个线程先拿到锁obj,导致其他线程处于等待锁的状态。
Java new Thread(new Runnable() { public void run() { synchronized (obj) { try { Thread.sleep(300000);//示例代码中第24行 } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); |
使用thread命令我们可以看到,thread-0 调用sleep()方法导致thread-1处于等待获取锁的状态。
getstatic -c classloader(hashcode) class_name field_name 指定类加载器,获取类的静态变量。(sc -d 类名取类加载器hashcode的方法)
Class相关
sc 查看JVM已加载的类信息
参数说明
-d | 查看类的详细信息 |
-f | 查看类的域(filed)信息 |
sc -d -f 查看类的详细信息及其域信息(可通过.*的方式模糊匹配)
sm 查看已加载类的方法信息(只能看到由当前类所声明的方法,无法看到父类的方法)
-d | 查看类的所有方法详细信息 |
class-pattern | 类名匹配 |
method-pattern | 方法名匹配 |
sm -d 类路径 方法名 (不写方法名,查看当前类的所有方法,不写-d 只显示方法名)
jad --source-only 反编译只显示源代码
classloader -t 获取classloader信息
retransform加载外部的class文件, 重新定义jvm已加载的类,实现jvm热更新。
说明:对象行为——>方法区——>字节码——>java源文件 java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行JVM上的应用程,实际上提供了一种虚拟机级别支持的 AOP 实现方式。java.lang.instrument.Instrumentation类提供retransformClasses方法,主要作用于对初始化过的clas重新转换,转换后的类文件字节作为类的新定义安装,实现方法区类定义的改变。 |
JVM热更新:
demo中启动一个springboot项目,在项目不做重启的情况下,实现jvm字节码热更新。
项目启动访问 curl -0 http://localhost:8080/user/10
此时想要修改业务代码(不能增删改类中的字段以及方法参数、方法名称及返回值,建议只做一些简单的修改,如日志打印)。
jad --source-only com.arthas.demo.controller.UserController > D:/arthas/UserController.java
反编译代码到指定的java文件,然后修改业务代码(添加业务日志等)。
mc -c 5910e440 D:/arthas/UserController.java -d D:/arthas/ 编译修改过的代码
retransform "D:\arthas\com\arthas\demo\controller\UserController.class" 热更新class
再次访问curl -0 http://localhost:8080/user/10 我们可以看到
说明:对象行为——>方法区——>字节码——>java源文件 java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行JVM上的应用程,实际上提供了一种虚拟机级别支持的 AOP 实现方式。java.lang.instrument.Instrumentation类提供retransformClasses方法,主要作用于对初始化过的clas重新转换,转换后的类文件字节作为类的新定义安装,实现方法区类定义的改变。 |
Java 热更新问题:
windows环境用java -jar xx启动的应用和arthas,成功attach。使用mc -c xx xx.java -d xx时报please confirm the application running in JDK not JRE。
解决方案:
1)将{jdk}/lib下的tool.jar文件复制到{jdk}/jre/lib
2)进入jre/bin目录,用java.exe -jar xx分别启动应用和arthas
如:
java.exe -jar C:\Users\hongbo.liu\arthas-boot.jar
java.exe -jar D:/arthas/arthas-0.0.1-SNAPSHOT.jar
字节码增强
请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行 stop 或将增强过的类执行 reset 命令。 |
monitor 方法执行监控:对匹配 class-pattern/method-pattern/condition-express的类、方法的调用进行监控。
monitor -c 5 com.demo.arthas.MonitorTest method1 以5秒为周期对method1进行监控
数据说明: total 总共调用次数,success成功次数 fail失败次数 avg-rt 平均响应时长 fail-rate 失败占比 |
monitor -b -c 5 com.demo.arthas.MonitorTest method1 "i>30" 按条件表达式在方法结束前筛选(不带-b 则表示在方法执行结束后筛选)
Watch 观察指定方法的调用情况。能观察到的范围为:返回值、抛出异常、入参,通过编写 OGNL 表达式进行对应变量的查看。
参数说明
|
我们可以根据指定方法的入参条件表达式和方法耗时条件进行过滤输出,如:下面示例中,只有方法的第一个参数值为‘ABC’且在抛出异常时才会输出。
watch com.demo.arthas.WatchTest method1 "{params,returnObj,throwExp}" 'params[0]=="ABC"' -e -x 2
耗时大于10ms并且第一个参数等于1才输出
watch com.demo.arthas.WatchTest method1 "{params,returnObj,throwExp}" '#cost>10 && params[0]=="ABC"' -x 5 -n 1
注意:同时指定了-s和-b,方法被调用一次,分别是方法被调用前,和返回之后注意,这里如果-n只设置成1,那么只会输出-b对应的输出,-s对应的输出由于没有次数了就无法输出。
watch com.demo.arthas.WatchTest method1 '{params,returnObj,throwExp}' -x 5 -n 2 -s -b
Trace 方法内部调用路径,并输出方法路径上的每个节点上耗时,能方便定位到 RT 高而导致的性能问题,但其每次只能跟踪一级方法的调用链路。(参考:Trace命令的实现原理)
参数说明
参数 | 说明 |
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
-n | 指定捕捉结果的次数(命令执行的次数) |
#cost | 方法执行耗时(watch/stack/trace这个三个命令都支持#cost) |
trace com.demo.arthas.TraceTest method1
我们看到method1调用了method2,在method2耗时较高,可以使用#cost进行筛选。
trace com.demo.arthas.TraceTest method1 '#cost > 1000'
注意: 1,默认情况下,trace不会包含jdk里的函数调用,如果希望trace jdk里的函数,需要显式设置--skipJDKMethod false。 |
race命令只会trace匹配到的函数里的子调用,并不会向下trace多层。因为trace是代价比较贵的,多层trace可能会导致最终要trace的类和函数非常多。
如果想要trace多层,可以再次调用trace到制定的方法,实现多层trace。
stack输出当前方法被调用的调用路径
参数说明
参数 | 说明 |
class-pattern | 类名表达式匹配 |
method-pattern | 方法名表达式匹配 |
condition-express | 条件表达式 |
-n | 指定捕捉结果的次数(命令执行的次数) |
特殊用法请参考:https://github.com/alibaba/arthas/issues/71 OGNL表达式官网:https://commons.apache.org/proper/commons-ognl/language-guide.html |
track com.demo.arthas.StackTest method2 '#cost > 1000' (同时也可指定params入参条件来筛选方法 如 params[0]==1等)
tt -t com.demo.arthas.TtTest method1
当你执行一个调用量不高的方法时,可以用 CTRL+C 中断 tt 命令记录的过程,但如果遇到调用量非常大的方法,瞬间就能将你的 JVM 内存撑爆。因此可以通过-n指定记录次数,Arthas 会主动中断tt命令的记录过程。 |
Arthas 很难区分出重载的方法 我们可以像watch、trace、stack 命令命令一样使用条件表达式进行区分(条件表达式也是用 OGNL 来编写,核心的判断对象依然是 Advice 对象,前边看到了很多条件表达式中,都使用了 params[0],有关这个变量的介绍,请参考官网文档表达式核心变量) 解决方法重载 tt -t com.demo.arthas.TtTest method1 params.length==1 通过制定参数个数的形式解决不同的方法签名,如果参数个数一样,你还可以这样写 tt -t com.demo.arthas.TtTest method1 'params[1] instanceof String' 解决指定参数 tt -t com.demo.arthas.TtTest method1 params[0].adsensekey=="ABC" |
总结:
以上是arthas常用命令使用方法的汇总,通过arthas可以帮助我们快速定位、分析诊断问题。
问题产生终归是有原因的,如果我们只知其一不知其二,很难从根本上解决问题。相反,如果我们对问题产生的底层原理有一定的了解的话,那么我们工作中可能会减少问题的出现。
例如:不合理的JVM参数设置(如:堆内存大小分配,垃圾回收器的选择等),导致OOM或者垃圾回收线程过多直接造成系统性能问题,再如,不合理的使用多线程以及过多的系统调用(如:频繁IO),导致cpu状态和线程上下文切换过于频繁,导致系统资源紧张或用户线程能够获取的cpu时间很少,导致程序运行性能下降,譬如以上问题,如果我们对jvm虚拟机有深入的了解,或者对操作系统如何分配系统资源以及如何进行线程的调度有深度的理解,就可能在很大程度上避免一些问题的产生。
相关文档
1,Arthas 用户文档:https://arthas.aliyun.com/
2,操作系统——进程、内存、IO管理:《操作系统》
3,深入理解java虚拟机——垃圾回收:https://www.cnblogs.com/czwbig/p/11127159.html
4,深入理解java Instrumentation:https://www.jianshu.com/p/5c62b71fd882
5,类加载机制:https://zhuanlan.zhihu.com/p/137857652
该文档使用了 产品需求文档 模板,点击立刻体验 |