[Arthas]
例子中用到的代码MathGame,也可到Arthas官网自行下载
package com.polaron.data.aop;import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;public class MathGame {private static Random random = new Random();private int illegalArgumentCount = 0;public static void main(String[] args) throws InterruptedException {MathGame game = new MathGame();while (true) {game.run();TimeUnit.SECONDS.sleep(1);}}public void run() throws InterruptedException {try {int number = random.nextInt()/10000;List<Integer> primeFactors = primeFactors(number);print(number, primeFactors);} catch (Exception e) {System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage());}}public static void print(int number, List<Integer> primeFactors) {StringBuffer sb = new StringBuffer(number + "=");for (int factor : primeFactors) {sb.append(factor).append('*');}if (sb.charAt(sb.length() - 1) == '*') {sb.deleteCharAt(sb.length() - 1);}System.out.println(sb);}public List<Integer> primeFactors(int number) {if (number < 2) {illegalArgumentCount++;throw new IllegalArgumentException("number is: " + number + ", need >= 2");}List<Integer> result = new ArrayList<Integer>();int i = 2;while (i <= number) {if (number % i == 0) {result.add(i);number = number / i;i = 2;} else {i++;}}return result;}
}
dashboard命令:
第一块区域:是当前运行的所有进程中的线程
第二块区域[Memory]:是内存的使用情况
第三块区域[Runtime]:java虚拟机运行时的一个状态
Thread命令
图中彩色部分是线程的状态,比如如果有BLOCKING的,就说明线程有可能死锁了。当然也可以直接使用命令来看死锁的线程。稍后介绍。
当前运行的所有进程中的线程,如果想看某个线程的状态就是用thread ID号就行,例如:
下面再来看一个如何使用thread命令直接查看死锁的例子:
上面例子中说明现在id为9的线程需要一把锁,但是锁的拥有者现在在id为10的Rose这个线程身上。这就导致id为9的这个线程一直拿不到锁,就死锁了。
jad命令:
如果想看某个类的代码,可以通过这个命令来反编译class文件,例如:
[ClassLoader]:表示程序的类加载器,这里看到有两个类加载器,一个是AppClassLoader,一个是ExtClassLoader,如果不懂的话,看以看我的jvm篇,里面是有讲解他们是什么。
[Location]:表示我们反编译的这个类的位置,下面绿色部分开始就是反编译后的代码了,这里只截取了一部分
Arthas基础命令:
1. cat命令
2. grep命令
管道符,可以从信息中过滤出你想查找的内容。
举例子:在Arthas命令行中输入sysprop可以显示出所有系统属性,假如现在你只想看java相关的属性信息,那么就可以使用这个命令
[arthas@24920]$ sysprop | grep java
结果如下:
3.pwd命令
显示当前目录
4.cls命令
清屏命令
5.session
查看当前会话,显示运行的java程序的进程id和SESSION_ID
6.reset
重置增强类,将Arthas增强过的类全部还原
PS:Arthas关闭时也会重置所有增强过的类,所谓增强就是我们通过Arthas修改过的类
7.version
当前正在运行的这个Arthas的版本号
8.quit
退出当前Arthas客户端
9.stop
关闭Arthas服务器,这样所有的客户端就都断了。例子:
用cmd同时运行arthas的jar包,然后在任何一个cmd窗口中执行stop命令,那么两个cmd中的Arthas都会关闭。
10.keymap
显示Arthas中的一些快捷键
以第一个为例,"/C-a"就是表示按键盘ctrl +a 的意思。比如现在在Arthas的命令行中输入:
[arthas@24920]$ 1231231
如果你想然光标跳到开头就可以使用ctrl + a快捷键,这样就不用使用方向键一下一下的按了。
另外截了一个黑马视频中的图:
11.history
查看我们之前敲过的所有命令。
12. jvm
显示jvm的一些信息,结果包括下面几部分:
[Runtime]:运行时的一些信息
[Class-Path]:类路径,对应于AppClassLoader这个加载器加载类的一些位置路径
[Boot-Class-Path]:Bootstrap类加载器加载类的路径
[Class-Loading]:所有已经加载的类的一些信息,例如加载的类的总数
[Memory]:内存信息。
还有其他的一部分可自行查阅。
13.sysprop
显示系统属性。默认显示所有属性。比如系统变量中设置的都会显示出来。
例子1: 只想查看某一项属性
[arthas@24920]$ sysprop java.home
sysprop java.homeKEY VALUE
-----------------------------------------------------------java.home C:\Program Files\Java\jdk1.8.0_51\jre
[arthas@24920]$
例子2:修改某个属性值|
[arthas@24920]$ sysprop user.country
sysprop user.countryKEY VALUE
---------------------------------------------user.country JP
[arthas@24920]$ sysprop user.country US
这样就把country改为US了。关闭Arthas后会自动重置为JP,不会影响程序源码
14.sysenv
显示系统的环境变量,默认显示所有
例子1:显示指定显示某个环境变量的内容
[arthas@24920]$ sysenv HOME
sysenv HOMEKEY VALUE
---------------------------------------------HOME C:\Users\li
15.vmoption
查看或者更新VM诊断相关的参数
可以看出全都是虚拟机相关的一些选项信息。然后你也可以显示某一个内容或者修改它
例子1:显示某一个内容
[arthas@24920]$ vmoption PrintGC
vmoption PrintGCKEY VALUE ORIGIN WRITEABLE
---------------------------------------------------------PrintGC false DEFAULT true
例子2:修改每个选项的值
[arthas@24920]$ vmoption PrintGC true
可以看出只需要在后面直接跟值,就可以修改了。
16.getstatic
查看类的静态属性(必须是程序执行的时候才能查看),现在一般都是用ognl来代替getstatic了。
语法:getstatic 类名 属性名
例子1:在一个循环中给一个Random类型的静态变量random赋值,然后查看这个静态变量
[arthas@34760]$ getstatic com.LogTest random
getstatic com.polaron.data.job.LogTest random
field: random
@Random[serialVersionUID=@Long[3905348978240129619],seed=@AtomicLong[145941670868477],multiplier=@Long[25214903917],addend=@Long[11],mask=@Long[281474976710655],DOUBLE_UNIT=@Double[1.1102230246251565E-16],BadBound=@String[bound must be positive],BadRange=@String[bound must be greater than origin],BadSize=@String[size must be non-negative],seedUniquifier=@AtomicLong[-7235497649666199876],nextNextGaussian=@Double[0.0],haveNextNextGaussian=@Boolean[false],serialPersistentFields=@ObjectStreamField[][isEmpty=false;size=3],unsafe=@Unsafe[sun.misc.Unsafe@47417fee],seedOffset=@Long[24],
]
17.ognl
一个表达式,3.05新增的功能,可以通过这个表达式直接调用或者操作我们代码 ,语法如下:
例子1:调用静态方法pirnt来打印东西
[arthas@34760]$ ognl '@java.lang.System@out.println("hello")'
ognl '@java.lang.System@out.println("hello")'
null
解释:
第一个@表示类,第二个@表示类中的一个静态属性(out在System类中是一个静态属性的名字,他是个对象),然后调用静态属性out的println方法。
这里看到的null是正确的,因为执行这个命令后显示的是调用方法后的返回值的内容,这个方法没有返回值,所以就为null了。而不是吧hello打印出来。
例子2:获取类的静态属性的值,等同于上面getstatic
[arthas@34760]$ ognl '@com.polaron.data.job.LogTest@random'
ognl '@com.polaron.data.job.LogTest@random'
@Random[serialVersionUID=@Long[3905348978240129619],seed=@AtomicLong[58908430518388],multiplier=@Long[25214903917],addend=@Long[11],mask=@Long[281474976710655],DOUBLE_UNIT=@Double[1.1102230246251565E-16],BadBound=@String[bound must be positive],BadRange=@String[bound must be greater than origin],BadSize=@String[size must be non-negative],seedUniquifier=@AtomicLong[5871221635705753676],nextNextGaussian=@Double[0.0],haveNextNextGaussian=@Boolean[false],serialPersistentFields=@ObjectStreamField[][isEmpty=false;size=3],unsafe=@Unsafe[sun.misc.Unsafe@47417fee],seedOffset=@Long[24],
]
例子3:获取属性值,然后放到list中
ognl '#value1=@System@getProperty("java.home"),{#value1}'
@ArrayList[@String[C:\Program Files\Java\jdk1.8.0_51\jre],
]
例子中通过#来设置一个变量接收值,然后通过{}作为list,让值放在里面。最后的@ArrayList就是最后返回的结果,说明是一个集合ArrayList,里面就是获取的值。
Class和ClassLoader相关的命令:
1.sc(search class)
查看jvm已经加载的类,默认还开启了子类匹配功能,也就是说当前了的子类也会被一同搜出来,要想精确的匹配,请打开options disable-sub-class true开关。
例子1:显示某个包下面的所有类
[arthas@34760]$ sc com.polaron.data.job.*
sc com.polaron.data.job.*
com.polaron.data.job.DataFetchJob
com.polaron.data.job.LogTest
com.polaron.data.job.ScheduledJob
Affect(row-cnt:3) cost in 19 ms.
如果想将每个类的详细信息也一同显示出来就在加一个参数 -d
sc com.polaron.data.job.* -dclass-info com.polaron.data.job.DataFetchJobcode-source /E:/company/POLARO/polaron/polaron-data-job/target/classes/name com.polaron.data.job.DataFetchJobisInterface falseisAnnotation falseisEnum falseisAnonymousClass falseisArray falseisLocalClass falseisMemberClass falseisPrimitive falseisSynthetic falsesimple-name DataFetchJobmodifier publicannotation org.springframework.stereotype.Componentinterfaces com.polaron.data.job.ScheduledJobsuper-class +-java.lang.Objectclass-loader +-sun.misc.Launcher$AppClassLoader@14dad5dc+-sun.misc.Launcher$ExtClassLoader@5276e6b0classLoaderHash 14dad5dc#这里只黏贴一个类的信息,其他的就省略了
如果还想将类中的属性显示出来,就在加个参数f就行。
2.sm(search method)
显示当前已经加载的所有Class的方法信息,它只能看当当前类的信息,没法看到父类的。
例子1:查看String这个类的方法信息
[arthas@34760]$ sm java.lang.String
sm java.lang.String
java.lang.String <init>([BII)V
java.lang.String <init>([BLjava/nio/charset/Charset;)V
java.lang.String <init>([BLjava/lang/String;)V
java.lang.String <init>([BIILjava/nio/charset/Charset;)V
java.lang.String <init>([BIILjava/lang/String;)V
java.lang.String <init>([CZ)V
java.lang.String <init>(Ljava/lang/StringBuilder;)V
java.lang.String <init>(Ljava/lang/StringBuffer;)V
java.lang.String <init>([B)V
java.lang.String <init>([III)V
java.lang.String <init>()V
java.lang.String <init>([C)V
java.lang.String <init>(Ljava/lang/String;)V
...
剩余结果省略,篇幅问题
...
例子2:查看方法的详细信息,其实就是在多加个参数-d就行
[arthas@34760]$ sm com.polaron.data.job.LogTest -d
sm com.polaron.data.job.LogTest -ddeclaring-class com.polaron.data.job.LogTestconstructor-name <init>modifier publicannotationparametersexceptions #该方法是否抛异常classLoaderHash 14dad5dcdeclaring-class com.polaron.data.job.LogTestmethod-name fetchDatamodifier publicannotation org.junit.Testparametersreturn voidexceptions java.lang.InterruptedExceptionclassLoaderHash 14dad5dc
3.jar
把字节码反编译成源码,在Arthas控制台显示出来
例子1:反编译String的class文件,查看其源码
[arthas@34760]$ jad java.lang.String
这个命令会连同一些classloader等其他信息也显示出来,如果不想显示这些,只想看源码,可以添加参数--source-only,如下:
[arthas@34760]$ jad java.lang.String --source-only
例子2:只想反编译类中的某个方法,并显示出来
[arthas@34760]$ jad java.lang.String trim --source-only
public String trim() {int n;int n2 = this.value.length;char[] cArray = this.value;for (n = 0; n < n2 && cArray[n] <= ' '; ++n) {}while (n < n2 && cArray[n2 - 1] <= ' ') {--n2;}return n > 0 || n2 < this.value.length ? this.substring(n, n2) : this;
}
4.mc
在内存中把源代码编译成字节码文件(.java->.class),跟javac差不多,只不过是直接在内存中操作。
例子1:在Arthas中编译一个Hello.java文件,这个文件与arthas.jar同级目录
[arthas@34760]$ mc Hello.java
#如果想指定生成的class文件的位置可以使用参数-d
[arthas@34760]$ mc Hello.java -d /usr/local/
5.redefine
把新生成的字节码文件在内存中执行,也就是可以直接加载class文件到jvm中运行。
注意:
- redefine后的原来的类将不能恢复
- reset命令对redefine的类无效,如果想重置,必须使用原始的class在redefine回去
- redefine命令和jad/watch/trace/monitor/tt等命令会冲突,执行redefine命令后在执行这几个命令会把redefine的字节码重置
限制:
- redefine不运行添加新的field和method
- 正在跑的函数,没有退出不能生效,例如下面代码中的System.out.println()只有在run中的才能生效
什么意思呢?就是说假如现在我们程序运行上面代码,我们想在不停止程序的情况下在代码的[2]处添加一个打印功能 System.out.println("redefine");我们该怎么办?这时就可以用上面的3个命令来实现public class Hello {public static void main(String [] args) {while(true) {run();[1]}}private static void run() {[2]} }
首先:用jad反编译这个Hello的class文件
然后:将反编译后的文件中添加上 System.out.println("redefine");这句话,然后用mc把修改后的这个反编译的java文件在编译成class文件
最后:使用redefine命令将这个又一次编译的class文件加载到内存中,这时程序在运行到run方法的时候就会多了System.out.println("redefine");这句话,就会打印了。但是如果加载[1]处,那么是不会生效的。如果还不懂可以看黑马的【Java线上诊断神器Arthas】P15将了这个用法。
6.dump
将应加载到jvm中的某个class文件保存到本地指定目录。默认保存目录是:logs/arthas/classdump/
例子:将LogTest这个class文件保存到默认目录
[arthas@34760]$ dump com.polaron.data.job.LogTest
dump com.polaron.data.job.LogTestHASHCODE CLASSLOADER LOCATION14dad5dc +-sun.misc.Launcher$AppClassLoader@14dad5dc C:\Users\liuhb\logs\+-sun.misc.Launcher$ExtClassLoader@5276e6b0 arthas\classdump\sun.misc.Launcher$AppClassLoader-14dad5dc\com\polaron\data\job\LogTest.class
Affect(row-cnt:1) cost in 81 ms.
7.classloader
作用:
- 将jvm中所有的类加载器classloader的信息统计显示出来
- 指定一个想要查找的文件的名称,然后可以显示出他所在的具体位置,对于ResourceNotFoundException比较有用。
例子1:
[arthas@34760]$ classloader
classloadername numberOfInstances loadedCountTotalsun.misc.Launcher$AppClassLoader 1 7221BootstrapClassLoader 1 3543com.taobao.arthas.agent.ArthasClassloader 1 2518java.FactoryURLClassLoader 1 672sun.reflect.DelegatingClassLoader 142 142sun.misc.Launcher$ExtClassLoader 1 75
Affect(row-cnt:6) cost in 13 ms.
例子2:查找某个资源文件他是在那个jar包中
[arthas@34760]$ classloader -c 540427c3 -r META-INF/MANIFEST.MF
classloader -c 540427c3 -r META-INF/MANIFEST.MFjar:file:/C:/Users/liuhb/.arthas/lib/3.5.4/arthas/arthas-core.jar!/META-INF/MANIFEST.MFAffect(row-cnt:1) cost in 21 ms.
例子3:查找String类在什么位置
[arthas@34760]$ classloader -c 540427c3 -r java/lang/String.class
classloader -c 540427c3 -r java/lang/String.classjar:file:/C:/Program%20Files/Java/jdk1.8.0_51/jre/lib/rt.jar!/java/lang/String.classAffect(row-cnt:1) cost in 10 ms.
注意:因为找的是class,所以要使用java/lang/String,不能java.lang.String.因为这里你是要找一个文件,他是一个资源。而不是jvm中的一个类。
例子4:显示某个类的信息
[arthas@34760]$ classloader -c 540427c3 -load java.lang.String
classloader -c 540427c3 -load java.lang.String
load class success.class-info java.lang.Stringcode-sourcename java.lang.StringisInterface falseisAnnotation falseisEnum falseisAnonymousClass falseisArray falseisLocalClass falseisMemberClass falseisPrimitive falseisSynthetic falsesimple-name Stringmodifier final,publicannotationinterfaces java.io.Serializable,java.lang.Comparable,java.lang.CharSequencesuper-class +-java.lang.Objectclass-loaderclassLoaderHash null
这里一定要注意与例子3的命令的区别,这里是查看类,而不是在找文件,所以要用java.lang.String.
8.monitor
作用:监视指定类中方法的执行情况,前提是这个方法要不停的中,如果他不执行你监视不到了。
它默认每120秒进行一次去统计一次,然后返回结果。
例子1:每五秒统计一个方法的执行情况(设置5秒统计一次,否则要等2分钟才能出现结果)
[arthas@23092]$ monitor com.polaron.data.aop.MathGame primeFactors -c 5
monitor com.polaron.data.aop.MathGame primeFactors -c 5
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 113 ms, listenerId: 1timestamp class method tota succ fail avg- faill ess rt(m -rats) e
-------------------------------------------------------------------------------2021-09-08 com.polaron.data primeFactors 5 5 0 0.68 0.0016:25:25 .aop.MathGame %
9.watch
观察指定方法的调用情况,能够观察到方法的返回值,抛出异常,入参,通过OGNL表达式进行对应变量的查看
例子1:观察MathGame中的primeFoactors方法出参和返回值,结果属性遍历深度为2
[arthas@44916]$ watch com.polaron.data.aop.MathGame primeFactors "{params,returnObj}" -x 2
nObj}" -x 2olaron.data.aop.MathGame primeFactors "{params,retur
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 90 ms, listenerId: 1
method=com.polaron.data.aop.MathGame.primeFactors location=AtExit
ts=2021-09-09 09:57:55; [cost=7.350308ms] result=@ArrayList[@Object[][@Integer[1],],@ArrayList[@Integer[5],@Integer[41],@Integer[739],],
]
method=com.polaron.data.aop.MathGame.primeFactors location=AtExceptionExit
ts=2021-09-09 09:57:56; [cost=0.184957ms] result=@ArrayList[@Object[][@Integer[-155594],],null,
]
解释:执行命令后,每秒会统计显示一次,命令中用到了OGNL表达式。双引号中就是OGNL表达式,{}中表示的是用ArrayList的形式返回我们要观察的内容,固定写法,想看参数就写param,如果不知道有几个参数,都想看的就跟我这样写params,看返回值就写returnObj。
从结果上看@Object就是我们的入参,@ArrayList就是方法的返回结果
关于深度参数-x,看下面的例子,两个例子比较看效果
例子2:显示深度为1
[arthas@44916]$ watch com.polaron.data.aop.MathGame primeFactors "{params,returnObj}" -x 1
nObj}" -x 1olaron.data.aop.MathGame primeFactors "{params,retur
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 25 ms, listenerId: 5
method=com.polaron.data.aop.MathGame.primeFactors location=AtExceptionExit
ts=2021-09-09 10:13:18; [cost=0.424315ms] result=@ArrayList[@Object[][isEmpty=false;size=1],null,
]
method=com.polaron.data.aop.MathGame.primeFactors location=AtExceptionExit
ts=2021-09-09 10:13:19; [cost=0.0304ms] result=@ArrayList[@Object[][isEmpty=false;size=1],null,
]
例子3:观察方法入参,对比前面的例子返回值为空(事件点为方法执行之前,因此获取不到返回值)
[arthas@44916]$ watch com.polaron.data.aop.MathGame primeFactors "{params,returnObj}" -x 2 -b
nObj}" -x 2 -bron.data.aop.MathGame primeFactors "{params,retur
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 20 ms, listenerId: 6
method=com.polaron.data.aop.MathGame.primeFactors location=AtEnter
ts=2021-09-09 10:22:17; [cost=0.079359ms] result=@ArrayList[@Object[][@Integer[-176655],],null,
]
这里主要考察-b参数的应用,用了-b后,就看不到方法的返回值了,所以我们在显示的结果中会看到为null,这是因为-b只会在方法运行之前统计一次信息,方法运行完成之后不会再统计,所以就拿不到返回值的信息,就只能显示为null了。
例子4:观察方法运行前后,对象中属性的值,使用target关键字来代表当前对象
[arthas@44916]$ watch com.polaron.data.aop.MathGame primeFactors "target" -x 2
-btch com.polaron.data.aop.MathGame primeFactors "target" -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 30 ms, listenerId: 7
method=com.polaron.data.aop.MathGame.primeFactors location=AtEnter
ts=2021-09-09 10:29:47; [cost=0.126078ms] result=@MathGame[random=@Random[serialVersionUID=@Long[3905348978240129619],seed=@AtomicLong[236689439131553],multiplier=@Long[25214903917],addend=@Long[11],mask=@Long[281474976710655],DOUBLE_UNIT=@Double[1.1102230246251565E-16],BadBound=@String[bound must be positive],BadRange=@String[bound must be greater than origin],BadSize=@String[size must be non-negative],seedUniquifier=@AtomicLong[3620162808252824828],nextNextGaussian=@Double[0.0],haveNextNextGaussian=@Boolean[false],serialPersistentFields=@ObjectStreamField[][isEmpty=false;size=3],unsafe=@Unsafe[sun.misc.Unsafe@17d10166],seedOffset=@Long[24],],illegalArgumentCount=@Integer[1184],
]
结果中可以看出这个命令可以观察代码中当前对象中所有的实例变量(属性),random和illegalArgumentCount就是两个实例变量的名字,可以参考上面源码找到。
如果只想看当前对象的某一个属性的内容,怎么办?例子如下:直接通过target.属性名就行
[arthas@44916]$ watch com.polaron.data.aop.MathGame primeFactors "target.illegalArgumentCount" -x 2
lArgumentCount" -x 2ta.aop.MathGame primeFactors "target.illega
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 24 ms, listenerId: 8method=com.polaron.data.aop.MathGame.primeFactors location=AtExit
ts=2021-09-09 10:34:39; [cost=0.319356ms] result=@Integer[1329]method=com.polaron.data.aop.MathGame.primeFactors location=AtExceptionExit
ts=2021-09-09 10:34:40; [cost=0.354555ms] result=@Integer[1330]method=com.polaron.data.aop.MathGame.primeFactors location=AtExit
ts=2021-09-09 10:34:41; [cost=0.0688ms] result=@Integer[1330]
可以看到结果中只显示出了一个属性的内容,这里只将最开始的3次返回信息粘贴了出来。
例子4:观察属性在方法调用前和调用后的变化。
[arthas@44916]$ watch com.polaron.data.aop.MathGame primeFactors "{params,target,returnObj}" -x 2 -b -s -n 2
t,returnObj}" -x 2 -b -s -n 2thGame primeFactors "{params,targe
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 33 ms, listenerId: 9
# 方法调用前
method=com.polaron.data.aop.MathGame.primeFactors location=AtEnter
ts=2021-09-09 10:46:39; [cost=0.125119ms] result=@ArrayList[@Object[][@Integer[9560],],@MathGame[random=@Random[java.util.Random@759ebb3d],illegalArgumentCount=@Integer[1651],],null,
]#方法调用后
method=com.polaron.data.aop.MathGame.primeFactors location=AtExit
ts=2021-09-09 10:46:39; [cost=2.66799961697118E8ms] result=@ArrayList[@Object[][@Integer[1],],@MathGame[random=@Random[java.util.Random@759ebb3d],illegalArgumentCount=@Integer[1651],],@ArrayList[@Integer[2],@Integer[2],@Integer[2],@Integer[5],@Integer[239],],
]
讲解:通过参数-n 2来设置观察2次,然后用-b和-s设定方法调用前和调用后分别观察一次。
从结果看方法调用前后对象中的数据有的发生了变化。这个命令很实用。
例子5:通过设定条件,将满足条件的显示出来。这里我们将第一个参数小于0的数据显示出来
[arthas@44916]$ watch com.polaron.data.aop.MathGame primeFactors "{params[0],returnObj}" "params[0]<0"
turnObj}" "params[0]<0"aop.MathGame primeFactors "{params[0],re
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 29 ms, listenerId: 10method=com.polaron.data.aop.MathGame.primeFactors location=AtExceptionExit
ts=2021-09-09 11:02:13; [cost=0.282877ms] result=@ArrayList[@Integer[-27225],null,
]method=com.polaron.data.aop.MathGame.primeFactors location=AtExceptionExit
ts=2021-09-09 11:02:15; [cost=0.079359ms] result=@ArrayList[@Integer[-196036],null,
]method=com.polaron.data.aop.MathGame.primeFactors location=AtExceptionExit
ts=2021-09-09 11:02:16; [cost=0.165118ms] result=@ArrayList[@Integer[-131578],null,
]
讲解:通过显示的结果看到@ArrayList中包含的就是参数和返回值两部分,第一部分@Integer就是参数的值,因为我们用了params[0],所以只显示第一个参数。null表示返回值的值,因为入参为负数所以返回值就为null(业务里这么做的)。我们发现显示的数据都是第一个参数小于0的(-27225)
10.trace
对一个方法中调用的其他方法进行跟踪,并且输出调用的每个方法的耗时。比如a方法调用b方法,就可以显示出b调用的时间。
例子1:
[arthas@44916]$ trace com.polaron.data.aop.MathGame run
trace com.polaron.data.aop.MathGame run
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 75 ms, listenerId: 11
`---ts=2021-09-09 11:15:22;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc`---[0.860469ms] com.polaron.data.aop.MathGame:run()+---[0.087039ms] com.polaron.data.aop.MathGame:primeFactors() #24`---[0.322556ms] com.polaron.data.aop.MathGame:print() #25`---ts=2021-09-09 11:15:23;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc`---[0.115839ms] com.polaron.data.aop.MathGame:run()+---[0.02912ms] com.polaron.data.aop.MathGame:primeFactors() #24`---[0.03808ms] com.polaron.data.aop.MathGame:print() #25
讲解:结合上面源码看,就可以看出显示出的内容就是源码中run调用过的方法,前面的[]中显示的就是每个方法的执行时间。--ts这一行显示了run方法是在哪个线程被调用的(thread-name=main),线程的id(id=1)等信息。而且会把最耗时的方法会用红色标识出来,我这里因为是粘贴的文字,所以没法显示颜色。
如果感觉一直返回结果,可以通过-n来指定显示的次数
例子2:默认是不会显示代码中调用的jdk方法的信息,如果想要显示通过添加--skipJDKMethod false设置
[arthas@44916]$ trace com.polaron.data.aop.MathGame run --skipJDKMethod false -n 2
n 2ce com.polaron.data.aop.MathGame run --skipJDKMethod false -
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 211 ms, listenerId: 12
`---ts=2021-09-09 11:27:20;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc`---[1.342703ms] com.polaron.data.aop.MathGame:run()+---[0.106878ms] java.util.Random:nextInt() #23+---[0.214717ms] com.polaron.data.aop.MathGame:primeFactors() #24 [throws Exception]+---[0.034879ms] java.lang.StringBuilder:<init>() #28+---[0.168958ms] java.lang.String:format() #28+---[min=0.006399ms,max=0.01056ms,total=0.016959ms,count=2] java.lang.StringBuilder:append() #28+---[0.01728ms] java.lang.Exception:getMessage() #28+---[0.00736ms] java.lang.StringBuilder:toString() #28`---[0.121918ms] java.io.PrintStream:println() #28`---ts=2021-09-09 11:27:21;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc`---[0.387195ms] com.polaron.data.aop.MathGame:run()+---[0.01088ms] java.util.Random:nextInt() #23+---[0.064959ms] com.polaron.data.aop.MathGame:primeFactors() #24`---[0.231677ms] com.polaron.data.aop.MathGame:print() #25
讲解:通过结果可以看出调用的jdk中的方法也显示出来了,例如#23,#28处,这里同时通过-n 2指定只显示2次。
例子3:通过#cost将执行时间大于某个值的方法显示出来,这里暂定为0.3秒
[arthas@44916]$ trace com.polaron.data.aop.MathGame run '#cost > .3' -n 2
trace com.polaron.data.aop.MathGame run '#cost > .3' -n 5
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 38 ms, listenerId: 13
`---ts=2021-09-09 11:32:10;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc`---[0.959668ms] com.polaron.data.aop.MathGame:run()`---[0.218238ms] com.polaron.data.aop.MathGame:primeFactors() #24 [throws Exception]`---ts=2021-09-09 11:32:14;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc`---[1.377903ms] com.polaron.data.aop.MathGame:run()+---[1.095986ms] com.polaron.data.aop.MathGame:primeFactors() #24`---[0.199677ms] com.polaron.data.aop.MathGame:print() #25
讲解:从结果可以看出只将大于0.3秒的显示出来,并且显示2次(-n 2),这里需要注意的是大于0.3值的是run()方法这一行的时间,而不是值run调用的方法的时间。而是值run总共的时间。一定要注意。
11.stack
栈的意思,显示当前方法调用的调用路径,意思就是比如你想知道到底是从哪些方法一步一步调用到自己的,也就是根据已知方法名称,显示出所有最开始的到已知方法之间的左右方法链路。就跟异常打印室的效果 一样,把所有途径的方法都打印出来。
与trace的区别:trace是显示你调用了谁,stack是现实你被谁调用了
例子1:
[arthas@44916]$ stack com.polaron.data.aop.MathGame primeFactors -n 1
stack com.polaron.data.aop.MathGame primeFactors -n 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 27 ms, listenerId: 14
ts=2021-09-09 14:03:04;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc@com.polaron.data.aop.MathGame.primeFactors()at com.polaron.data.aop.MathGame.run(MathGame.java:24)at com.polaron.data.aop.MathGame.main(null:-1)
可以看出primeFactors方法是由main->run->primeFactors
例子2:通过条件表达式来过滤,将第一个参数小于0的显示出来。
[arthas@44916]$ stack com.polaron.data.aop.MathGame primeFactors -n 1
stack com.polaron.data.aop.MathGame primeFactors -n 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 27 ms, listenerId: 14
ts=2021-09-09 14:03:04;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc@com.polaron.data.aop.MathGame.primeFactors()at com.polaron.data.aop.MathGame.run(MathGame.java:24)at com.polaron.data.aop.MathGame.main(null:-1)Command execution times exceed limit: 1, so command will exit. You can set it with -n option.
[arthas@44916]$ stack com.polaron.data.aop.MathGame primeFactors 'params[0]<0' -n 2
-n 2k com.polaron.data.aop.MathGame primeFactors 'params[0]<0'
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 35 ms, listenerId: 15
ts=2021-09-09 14:26:39;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc@com.polaron.data.aop.MathGame.primeFactors()at com.polaron.data.aop.MathGame.run(MathGame.java:24)at com.polaron.data.aop.MathGame.main(null:-1)ts=2021-09-09 14:26:40;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc@com.polaron.data.aop.MathGame.primeFactors()at com.polaron.data.aop.MathGame.run(MathGame.java:24)at com.polaron.data.aop.MathGame.main(null:-1)
讲解:使用条件还是需要通过OGNL表达式,命令中单引号中的就是OGNL,params[0]表示方法中的第一个参数。
例子3:将耗时大于0.5秒的显示出来
[arthas@44916]$ stack com.polaron.data.aop.MathGame primeFactors '#cost>0.5' -n 22ack com.polaron.data.aop.MathGame primeFactors '#cost>0.5' -n
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 31 ms, listenerId: 16
ts=2021-09-09 14:35:21;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc@com.polaron.data.aop.MathGame.primeFactors()at com.polaron.data.aop.MathGame.run(MathGame.java:24)at com.polaron.data.aop.MathGame.main(null:-1)
讲解:通过结果来看,这个命令虽然用时间进行了过滤,但是却没法显示出具体的耗时,所以要显示耗时就要用track命令。
12.tt(time-tunnel)
记录并显示指定方法每次被调用时的入参和返回值信息
例子1:
[arthas@44916]$ tt -t com.polaron.data.aop.MathGame primeFactors -n 2
tt -t com.polaron.data.aop.MathGame primeFactors -n 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 30 ms, listenerId: 18IND TIMESTAMP COST IS- IS- OBJECT CLASS METHODEX (ms) RET EXP
-------------------------------------------------------------------------------100 2021-09-09 0.29 fal tru 0x3d012 MathGame primeFactors0 14:44:46 3436 se e ddd100 2021-09-09 0.38 tru fal 0x3d012 MathGame primeFactors1 14:44:47 0795 e se ddd
讲解:
上面这个图主要讲的就是方法重载时,你可以通过命令形式解决使用哪个方法 。通过params[n]表示第几个参数。
例子2:查看上一次用tt命令显示出的内容
[arthas@44916]$ tt -l #这个命令就可以直接将例子1的结构直接在显示出来
例子3:如果只想看某个特定方法的信息
[arthas@44916]$ tt -s 'method.name=="primeFactors"'
#单引号中的内容是OGNL表达式
例子4:通过索引index来显示
[arthas@44916]$ tt -i 1002
tt -i 1002 #1002就是tt -t后显示的结果中的index那一列的值INDEX 1002GMT-CREATE 2021-09-09 15:01:16COST(ms) 0.095358OBJECT 0x3d012dddCLASS com.polaron.data.aop.MathGameMETHOD primeFactorsIS-RETURN trueIS-EXCEPTION falsePARAMETERS[0] @Integer[124902]RETURN-OBJ @ArrayList[@Integer[2],@Integer[3],@Integer[3],@Integer[3],@Integer[3],@Integer[3],@Integer[257],]
Affect(row-cnt:1) cost in 2 ms.
PARAMENTS[0]表示方法的第一个参数
RETURN-OBJ就是方法的返回值内容
Arthas选项的一些设置,也就是一些全局开关
1.option
profile 火焰图
所谓火焰图就是arthas定时进行采样,一般比如对cpu进行采样,内存采样,然后以火焰形式显示。
语法:profiler 命令 [命令参数]
例子:
首先启动profiler,作用就是告诉arthas现在开始采样,默认是对CPU进行采样
除了CPU之外的东西可以通过命令list来看
当我们想查看收集了多少个采样样本的时候,可以执行命令如下:
如果我们想查看已经采用了多长时间了,用下面命令:
最后,如果我们想看最终的采样的火焰图,我们只要停止profiler就行,如下:
可以看到它告诉你火焰图保存在什么位置了。是一个svg文件。样子大约如下:
如果不想生成svg想生成html文件,如下操作:
上面便是一些Arthas的命令,
提示注意:如果我们开发的是web应用,那么当我们想使用watch,trace,stack等命令时,一定是先把命令打上,然后在在应用中执行一次请求,这样arthas才能返回统计的内容。如果你刚敲上命令回车后,你是看不到任何统计信息的,他只能堵塞在那,直到你调用一次代码才行。