原创声明:作者:Arnold.zhao 博客园地址:https://www.cnblogs.com/zh94
Arthas
关于Arthas的基本概念可参考:
https://github.com/alibaba/arthas/blob/master/README_CN.md
关于Arthas等相关JVM Debug工具的实现原理可参考:
https://www.cnblogs.com/meituantech/p/11670535.html
https://blog.csdn.net/u010862794/article/details/87773434
快速使用
启动Arthas
$ $ curl -O https://alibaba.github.io/arthas/arthas-demo.jar
$ $ java -jar arthas-boot.jar
* [1]: 35542
[2]: 71560 arthas-demo.jar
1
此处选择序号来表示当前所要链接的进程;
dashboard
介绍:输入dashboard,按回车/enter,会展示当前进程的信息,按ctrl+c可以中断执行。
优势:当在生产环境中我们需要查看当前进程内线程的运行情况时需要使用Linux原生的 top -p PID -H来获取线程信息;当需要查看进程的GC情况时,则需要使用jstat 或 jconsole等工具来监控内存的变化;而此处dashboard控制台一键集成,想要实时观察GC和线程信息则再也不用连续的观察多个面板了;
thread
介绍:通过使用dashboard获取到当前所有的线程运行信息后,使用thread 1打印当前ID为1的线程栈信息
优势:当生产环境一个线程CPU飙高,或者锁死的情况时,我们常规的做法是 jstack 快速打印当前进程的线程堆栈信息,然后再具体分析对应的堆栈日志来依次观察具体的问题点;而此处当我们使用dashboard捕获到具体的异常线程时,直接使用Arthas 的thread命令便可以直接打印当前指定线程的栈信息,快速定位具体哪块方法体所引起的问题;
[arthas@23846]$ thread 1
"main" Id=1 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at demo.MathGame.main(MathGame.java:17)
sc
介绍:查看当前JVM中所加载的类信息,此处表示查看当前MatchGame类的具体信息(支持正则匹配)
优势:通过使用sc命令可以排查你当前项目中的某些类是否被加载进来,或者在Spring中某些依赖是否被正确加载等问题
[arthas@23846]$ sc -d *MathGame (-d表示查看detail详情,关于当前sc命令的options参数,可以使用 sc -h 获取具体的参数格式)
class-info demo.MathGame (表示当前类的包路径信息)
code-source /opt/shengheApp/arthas/arthas-demo.jar(当前类加载自那个jar包中)
name demo.MathGame
isInterface false (是否是接口)
isAnnotation false (是否是注解)
isEnum false (是否是枚举等等)
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name MathGame (类简称)
modifier public (当前类的访问修饰符)
annotation
interfaces
super-class +-java.lang.Object (当前类的父类)
class-loader +-sun.misc.Launcher$AppClassLoader@70dea4e(当前类加载自那个ClassLoader)
+-sun.misc.Launcher$ExtClassLoader@708ab80c
classLoaderHash 70dea4e
Affect(row-cnt:1) cost in 7 ms.
jad
介绍:使用jad来反编译代码,类似于我们桌面端常用的 jd-gui等工具
优势:生产环境更新代码后,很可能会出现代码更新后不生效等问题(比如本地代码打包未重新编译等),通过使用jad反编译代码后,可以快速确认当前线上代码是否是本地的最新代码等问题;并且jad反编译后的代码可读性更强,相比于jdk自带的javap反编译命令要好很多
[arthas@23846]$ jad demo.MathGame
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@70dea4e
+-sun.misc.Launcher$ExtClassLoader@708ab80c
Location:
/opt/shengheApp/arthas/arthas-demo.jar
/*
* Decompiled with CFR.
*/
package demo;
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();
public int illegalArgumentCount = 0;
public List<Integer> primeFactors(int number) {
if (number < 2) {
++this.illegalArgumentCount;
throw new IllegalArgumentException("number is: " + number + ", need >= 2");
}
ArrayList<Integer> result = new ArrayList<Integer>();
int i = 2;
while (i <= number) {
if (number % i == 0) {
result.add(i);
number /= i;
i = 2;
continue;
}
++i;
}
return result;
}
原创声明:作者:Arnold.zhao 博客园地址:https://www.cnblogs.com/zh94
watch(重要)
通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行 stop 或将增强过的类执行 reset 命令。
介绍:方法执行数据观测,可以轻松的观察到当前指定方法的调用情况以及通过书写特定的OGNL表达式来进行对应变量的查看,能观察到的范围为当前方法的:返回值、抛出异常、入参
关于OGNL表达式的说明可参考:
官网:https://commons.apache.org/proper/commons-ognl/language-guide.html
博客:https://cloud.tencent.com/developer/article/1554323
重要说明:
当前watch的命令实际上只是给当前所要被观察的方法增加一个切面的代码来实现后续的统计;
而我们通过切面则可以捕获到当前被执行方法的以下参数:如方法的入参是什么?方法的出参是什么?方法的执行结果是否抛出异常,抛出的异常栈是什么等信息;
那么这些所有被捕获到的观察信息,实际上都被封装到了一个通用的通知对象Advice的类中,当前Advice类的属性及包含了方法的入参,也包含了方法的出参等数据
而我们所书写的OGNL表达式,实际上就是针对于当前的Advice的类属性而写,比如此时OGNL表达式写的是:{params,returnObj} ,那么此OGNL所表示的含义则是输出当前被观察类的入参(params),和返回结果(returnObj)的内容;
关于当前Advice通知类的具体参数都有哪些,可以参考该链接:https://arthas.gitee.io/advice-class.html
watch命令格式如下:
优势:想要调试某个线上服务的偶发性BUG,但是又不能重启线上服务的情况下,通过对当前所需监控的方法以及各个被调用的嵌套方法都增加watch监控的方式以此来实现动态DEBUG的效果
劣势:不可以在指定的代码行增加动态代码的方式来达到DEBUG的效果;BTrace可以实现更佳的服务器线上DEBUG效果,但使用方式相对更加重一些
执行命令解读:
watch demo.MathGame【指定类路径】 primeFactors【指定被观察方法名】 "{params,returnObj}"【指定OGNL表达式】 "params[0]<0" 【条件表达式,表示当前入参<0时则触发检测】 -x 3【指定输出结果的遍历深度】 -f【表示在方法结束之后触发观察】
[arthas@23846]$ watch demo.MathGame primeFactors "{params,returnObj}" "params[0]<0" -x 3 -f
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 36 ms.
ts=2020-04-29 15:47:59; [cost=0.197494ms] result=@ArrayList[
@Object[][
@Integer[-56580], (当前被观察方法的入参)
],
null, (当前被观察方法的出参)
]
ts=2020-04-29 15:48:03; [cost=0.073761ms] result=@ArrayList[
@Object[][
@Integer[-161310], (当前被观察方法的入参)
],
null, (当前被观察方法的出参)
]
由于我们上面执行命令时设置的条件表达式是只针对 入参 < 0 的执行方法才进行输出,所以以上两个输出的结果全部是入参为负数时的数据
reset
介绍:用于还原指定的增强类,使用watch等命令对指定的方法进行了相关debug诊断完以后,则需要使用reset进行类的还原
还原指定类:
[arthas@23846]$ reset demo.MathGame
Affect(class-cnt:0 , method-cnt:0) cost in 0 ms.
还原所有类:
[arthas@23846]$ reset
Affect(class-cnt:0 , method-cnt:0) cost in 1 ms.