Arthas使⽤教程[转载]
什么是 Arthas
摘录⼀段官⽅ Github 上的简介
Arthas 是Alibaba开源的Java诊断⼯具,深受开发者喜爱。
当你遇到以下类似问题⽽束⼿⽆策时,Arthas 可以帮助你解决:
这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
我改的代码为什么没有执⾏到?难道是我没 commit?分⽀搞错了?
遇到问题⽆法在线上 debug,难道只能通过加⽇志再重新发布吗?
线上遇到某个⽤户的数据处理有问题,但线上同样⽆法 debug,线下⽆法重现!
是否有⼀个全局视⾓来查看系统的运⾏状况?
有什么办法可以监控到JVM的实时运⾏状态?
Arthas ⽀持JDK 6+,⽀持Linux/Mac/Windows,采⽤命令⾏交互模式,同时提供丰富的 Tab ⾃动补全功能,进⼀步⽅便进⾏问题的定位和诊断。
Arthas 基于哪些⼯具开发⽽来
greys-anatomy: Arthas代码基于Greys⼆次开发⽽来,⾮常感谢Greys之前所有的⼯作,以及Greys原作者对Arthas提出的意见和建议!
termd: Arthas的命令⾏实现基于termd开发,是⼀款优秀的命令⾏程序开发框架,感谢termd提供了优秀的框架。
crash: Arthas的⽂本渲染功能基于crash中的⽂本渲染功能开发,可以从这⾥看到源码,感谢crash在这⽅⾯所做的优秀⼯作。
cli: Arthas的命令⾏界⾯基于vert.x提供的cli库进⾏开发,感谢vert.x在这⽅⾯做的优秀⼯作。
compiler: Arthas⾥的内存编绎器代码来源
Apache Commons Net: Arthas⾥的Telnet Client代码来源
JavaAgent:运⾏在 main⽅法之前的,它内定的⽅法名叫 premain ,也就是说先执⾏ premain ⽅法然后再执⾏ main ⽅法ASM:⼀个通⽤的Java字节码操作和分析框架。它可以⽤于修改现有的类或直接以⼆进制形式动态⽣成类。ASM提供了⼀些常见的字节码转换和分析算法,可以从它们构建定制的复杂转换和代码分析⼯具。ASM提供了与其他Java字节码框架类似的功能,但是主要关注性能。因为它被设计和实现得尽可能⼩和快,所以⾮常适合在动态系统中使⽤(当然也可以以静态⽅式使⽤,例如在编译器中)
唯⼀可惜的是,arthas没有jmap -histo功能,此功能可以在线打印所有对象的数量和占⽤内存⼤⼩
启动
启动命令
java -jar arthas-boot.jar
不⽤arthas时⼀定要正常退出,命令如下:
quit:只是退出当前的连接。Attach到⽬标进程上的arthas还会继续运⾏,端⼝会保持开放,下次连接时执⾏java -jar arthas-boot.jar可以直接连接上。
exit:和quit命令⼀样的功能;
stop:完全退出arthas,
如果是⾮正常退出,会报下⾯的错误,提⽰端⼝占⽤。原因是上次连接了⼀个进程,未正常退出。
[ERROR] The telnet port 3658 is used by process 3804 instead of target process 15043, you will connect to an unexpected process.
[ERROR] 1. Try to restart arthas-boot, select process 3804, shutdown it first with running the 'stop' command.
[ERROR] 2. Or try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 3658 -c "stop"
[ERROR] 3. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1
注意: arthas依赖jdk的环境变量,也依赖⼀些jdk⾃带的⼯具,⽐如 jps,如果服务器上只有jre环境⽽没有jdk环境的话,是没有jps的,所以arthas也会报错;
arthas所有命令
启动arthas后,在命令⾏输⼊help,即可查看命令帮助信息、当前arthas版本⽀持的指令,或者查看具体指令的使⽤说明。所有的命令如下:基础命令
命令说明
cls清空当前屏幕区域。
session查看当前会话的信息,显⽰当前绑定的pid以及会话id。
reset重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端stop时会重置所有增强过的类
version输出当前⽬标 Java 进程所加载的 Arthas 版本号
history打印历史命令,打印出你在使⽤arthas过程中输⼊了哪些命令。
keymap输出arthas的快捷键映射表:
进阶命令
命令说明
dashboard查看当前系统的实时数据⾯板,例如:服务器thread信息、内存memory、GC回收等情况
thread查看线程的堆栈信息
jvm打印出jvm的信息,包括参数和变量,以及⽤的jvm名字、系统等等
sysprop查看当前JVM的系统属性(System Property)
sysenv查看当前JVM的环境属性(System Environment Variables)
vmoption查看,更新VM诊断相关的参数
perfcounter查看当前JVM的 Perf Counter信息
logger查看logger信息,更新logger level(级别)
mbean这个命令可以便捷的查看或监控 Mbean 的属性信息。
getstatic通过getstatic命令可以⽅便的查看类的静态属性。使⽤⽅法为getstatic class_name field_name,推荐直接使⽤ognl命令,更加灵活。ognl执⾏ognl表达式,此命令可动态执⾏代码
sc查看JVM已加载的类信息,可以查看类在哪个jar包⾥⾯
sm查看已加载类的⽅法信息
dump dump 已加载类的 bytecode 到特定⽬录
heapdump dump java heap, 类似jmap命令的heap dump功能。
vmtool vmtool 利⽤JVMTI接⼝,实现查询内存对象,强制GC等功能。
jad反编译指定已加载类的源码
classloader查看classloader的继承树,urls,类加载信息
mc Memory Compiler/内存编译器,编译.java⽂件⽣成.class。
retransform加载外部的.class⽂件,动态重新加载 jvm已加载的类。
redefine加载外部的.class⽂件,redefine jvm已加载的类。这个⽅式只是修改运⾏时内存,class⽂件并没有改变,服务重启就失效了,推荐使⽤ retransform 命令
monitor⽅法执⾏监控
watch⽅法执⾏数据观测,让你能⽅便的观察到指定⽅法的调⽤情况。能观察到的范围为:返回值、抛出异常、⼊参,通过编写OGNL 表达式进⾏对应变量的查看。
trace⽅法内部调⽤路径,并输出⽅法路径上的每个节点上耗时,trace 命令能主动搜索 class-pattern/method-pattern 对应的⽅法调⽤路径,渲染和统计整个调⽤链路上的所有性能开销和追踪调⽤链路。
stack输出当前⽅法被调⽤的调⽤路径退出grep命令
tt⽅法执⾏数据的时空隧道,记录下指定⽅法每次调⽤的⼊参和返回信息,并能对这些不同的时间下调⽤进⾏观测
profiler profiler 命令⽀持⽣成应⽤热点的⽕焰图。本质上是通过不断的采样,然后把收集到的采样结果⽣成⽕焰图。
cat打印⽂件内容,和linux⾥的cat命令类似。
echo打印参数,和linux⾥的echo命令类似
grep类似传统的grep命令
base64base64编码转换,和linux⾥的 base64 命令类似。
tee类似传统的tee命令, ⽤于读取标准输⼊的数据,并将其内容输出成⽂件。
pwd返回当前的⼯作⽬录,和linux命令类似
auth验证当前会话
options系统的配置开关,可开启和关闭
命令介绍
这⾥主要介绍常⽤的arthas命令,不会每个命令都介绍⼀遍,有些命令是linux继承过来的,也没必要介绍。
session 查看当前会话的信息
这个命令⾮常简单,就只是打印进程的pid和会话id,注意是java的进程id,不是arthas的进程id;
[arthas@12771]$ session
Name        Value
--------------------------------------------------
JAVA_PID    12771
SESSION_ID  41d19baa-2064-442a-acf1-b8ac11801265
history 打印命令历史
启动arthas后,你输⼊过的命令都会被记录起来,history则会打印出你的输⼊历史
history 5:查看最近执⾏的5条指令
history -c:清空输⼊的历史指令
quit 退出当前arthas客户端
只是退出当前 Arthas 客户端,其他 Arthas 客户端不受影响。等同于exit、logout、q三个指令。
stop 关闭 Arthas 服务端
执⾏stop指令后,所有 Arthas 客户端全部退出。关闭Arthas服务器之前,会重置掉所有做过的增强类。但是⽤redefine重加载的类内容不会被重置。
dashboard命令查看当前系统的实时数据⾯板
查看当前系统的实时数据⾯板,例如:服务器thread(线程)信息、内存memory、GC回收等情况
数据列说明
ID: Java级别的线程ID,注意这个ID不能跟jstack中的nativeID⼀⼀对应。
NAME: 线程名
GROUP: 线程组名
PRIORITY: 线程优先级, 1~10之间的数字,越⼤表⽰优先级越⾼
STATE: 线程的状态
CPU%: 线程的cpu使⽤率。⽐如采样间隔1000ms,某个线程的增量cpu时间为100ms,则cpu使⽤率=100/1000=10%
DELTA_TIME: 上次采样之后线程运⾏增量CPU时间,数据格式为秒
TIME: 线程运⾏总CPU时间,数据格式为分:秒
INTERRUPTED: 线程当前的中断位状态
DAEMON: 是否是daemon线程
JVM内部线程
Java 8之后⽀持获取JVM内部线程CPU时间,这些线程只有名称和CPU时间,没有ID及状态等信息(显⽰ID为-1)。通过内部线程可以观测到JVM活动,如GC、JIT编译等占⽤CPU情况,⽅便了解JVM整体运⾏状况。
设置刷新间隔和次数
另外,⾯板会默认会每5秒刷新⼀次,并且会⼀直刷新下去,如果想要指定刷新次数和间隔时间,可以这么写:
dashboard dashboard -i 1000 -n 2
-i表⽰刷新的间隔时间,单位(毫秒),-n表表⽰查询的次数,到达指定次数后,⾃动退出dashboard⾯板;
thread 查看线程的堆栈信息
当没有参数时,默认显⽰第⼀页的线程信息:thread
查看某个线程的堆栈:thread 13,13为线程的id
[arthas@12771]$ thread 13
"ContainerBackgroundProcessor[StandardEngine[Tomcat]]" Id=13 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at org.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1368)
at java.lang.Thread.run(Thread.java:748)
除此之外,thread还有其他的⽤法
thread -n 5:打印前5个最忙的线程并打印堆栈
thread -all:显⽰所有匹配的线程
thread -n 3 -i 1000:列出1000ms内最忙的3个线程栈
thread –state WAITING,查看指定状态的线程,(TIMED_WAITI、WAITING、RUNNABLE等等)
thread -b:出阻塞其他线程的线程,当出现死锁后,会提⽰你出现死锁的位置,代码如下
static Object obj = new Object();
public static void main(String[] args) throws InterruptedException, IOException {
new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (obj) {
System.out.println("我是第⼀个线程");
Thread.sleep(100000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"yexindong-one").start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
System.out.println("我是第⼆个线程");
}
}
},"yexindong-two").start();
}
jvm 查看当前JVM信息
jvm显⽰jvm的数据汇总,具体内容分为以下⼏块
runtime:包括jvm开始时间,启动参数,class_path等
class-loading :已加载类的数量,总共加载类数量,已卸载类的数量
garbage-collectors:显⽰使⽤的垃圾收集器及垃圾收集次数
memory :堆内存空间使⽤情况
thread:线程总数,守护线程数,死锁数量
thread线程相关信息如下
COUNT: JVM当前活跃的线程数
DAEMON-COUNT: JVM当前活跃的守护线程数
PEAK-COUNT: 从JVM启动开始曾经活着的最⼤线程数
STARTED-COUNT: 从JVM启动开始总共启动过的线程次数
DEADLOCK-COUNT: JVM当前死锁的线程数
vmoption 查看/更新虚拟机参数
这个命令可以看到我们的java项⽬在运⾏时设置了哪些参数,命令没有参数时会打印所有的vm参数[arthas@12771]$ vmoption
KEY                                  VALUE                                ORIGIN                              WRITEABLE                          ----------------------------------------------------------------------------------------------------------------------------------------------------
HeapDumpBeforeFullGC                false DEFAULT true
HeapDumpAfterFullGC                  false DEFAULT true
HeapDumpOnOutOfMemoryError          false DEFAULT true
HeapDumpPath                                                              DEFAULT true
CMSAbortablePrecleanWaitMillis      100                                  DEFAULT true
CMSWaitDuration                      2000                                DEFAULT true
CMSTriggerInterval                  -1                                  DEFAULT true
PrintGC                              false DEFAULT true
PrintGCDetails                      false DEFAULT true
PrintGCDateStamps                    false DEFAULT true
PrintGCTimeStamps                    false DEFAULT true
PrintGCID                            false DEFAULT true
PrintClassHistogramBeforeFullGC      false DEFAULT true
PrintClassHistogramAfterFullGC      false DEFAULT true
PrintClassHistogram                  false DEFAULT true
MinHeapFreeRatio                    40                                  DEFAULT true
MaxHeapFreeRatio                    70                                  DEFAULT true
PrintConcurrentLocks                false DEFAULT true
除此之外,还可查看单个参数和动态设置参数功能,这⾥可以动态更新参数,不需要重启java进程;
vmoption PrintGCDetails查看指定的vm参数
vmoption PrintGCDetails true更新指定的vm参数
ognl 动态执⾏代码
调⽤静态⽅法:ognl '@java.lang.System@out.println("hello yexindong")',执⾏后,可以看到控制台打印了hello yexindong;
sc 查看jvm已加载的类信息
简单⽤法st.Test,如果jvm已加载这个类,则会打印出该类的全类名,
[arthas@3568]$ st.Test
Affect(row-cnt:1) cost in 4 ms.
但是这样的信息太过于简陋了,加上-d参数可以打印类的详细信息,并且还可以查看你这个类在哪个jar包⾥⾯,命令如下sc -st.Test
打印结果如下
[arthas@3568]$ sc -d *.Test
class-info        st.Test
code-source      /Users/yexindong/Documents/java/java_project/Test/target/cla
sses/
name              st.Test
isInterface      false
isAnnotation      false
isEnum            false
isAnonymousClass  false
isArray          false
isLocalClass      false
isMemberClass    false
isPrimitive      false
isSynthetic      false
simple-name      Test
modifier          public
annotation
interfaces
super-class      +-java.lang.Object
class-loader      +-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@3df8ac7c
classLoaderHash  18b4aac2
Affect(row-cnt:1) cost in 17 ms.
除此之外,还可以使⽤模糊查询
sc -d *.Test:查询所有类名为Test的类
sc -d com.*:查询com包下⾯所有的类,会往所有的⼦包遍历
sm 查看已加载的类⽅法信息
查询某个类下所有的⽅法
[arthas@3568]$ st.Test
Affect(row-cnt:2) cost in 4 ms.