一、概述
1.1 Arthas(阿尔萨斯) 能为你做什么?
Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
1.这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
2.我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
3.遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
4.线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
5.是否有一个全局视角来查看系统的运行状况?
6.有什么办法可以监控到JVM的实时运行状态?
7.怎么快速定位应用的热点,生成火焰图?
1.2 运行环境要求
Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。
二、快速安装
下载arthas-boot.jar,然后用java -jar的方式启动:
命令
java
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar
注:在运行第2条命令之前,先运行一个java进程在内存中,不然会出现找不到java进程的错误。 打印帮助信息
java
java -jar arthas-boot.jar -h
如果下载速度比较慢,可以使用aliyun的镜像:
java
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
Windows下安装
1.在c:\下创建目录arthas,在windows命令窗口下,使用curl命令下载阿里服务器上的jar包,大小108k
2.使用java启动arthas-boot.jar,来安装arthas,大小约10M。运行此命令会发现java进程,输入1按回车。则自动从远程主机上下载arthas到本地目录
3.查看安装好的目录
C:\Users\Administrator.arthas\lib\3.1.7\arthas\
小结 1.下载arthas-boot.jar包
2.执行arthas-boo.jar包,前提是必须要有java进程在运行。第一次执行这个jar包,会自动从服务器上下载arthas,大小是11M
三、从Maven仓库下载全量包
如果下载速度比较慢,可以尝试用阿里云的镜像仓库
步骤 1.比如要下载3.1.7版本,下载的url是: maven.aliyun.com/repository/...
maven.aliyun.com/repository/...
2.解压后,在文件夹里有
arthas-boot.jar,直接用java -jar的方式启动:
cmd
java -jar arthas-boot.jar
注:如果是Linux,可以使用以下命令解压到指定的arthas目录
cmd
unzip -d arthas arthas-packaging-3.1.7-bin.zip
小结
1.在Linux下在线安装的方式与在Windows下的安装相同
2.如果要使用离线的安装方式,先下载完成的zip到本地,再解压到任意的目录即可
卸载
在 Linux/Unix/Mac 平台
删除下面文件:
cmd
rm -rf ~/.arthas/
rm -rf ~/logs/arthas
Windows平台
直接删除user home下面的.arthas和logs/arthas目录 1.安装主目录
2.日志记录目录
小结
因为jar包是绿色,要卸载的话,直接删除2个目录 .arthas安装目录 logs的日志记录目录
四、快速入门:attach一个进程
目标:通过案例快速入门
1.执行一个jar包
2.通过arthas来attach粘附
步骤
- 准备代码 以下是一个简单的Java程序,每隔一秒生成一个随机数,再执行质因数分解,并打印出分解结果。代码的内容不用理会这不是现在关注的点。
java
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 static void main(String[] args) throws InterruptedException {
MathGame game = new MathGame();
//死循环,每过1秒调用1次下面的方法(不是开启一个线程)
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);
}
//计算number的质因数分解
public List<Integer> primeFactors(int number) {
//如果小于2,则抛出异常,并且计数加1
if (number < 2) {
illegalArgumentCount++;
throw new IllegalArgumentException("number is: " + number + ", need >= 2");
}
//用于保存每个质数
List<Integer> result = new ArrayList<Integer>();
//分解过程,从2开始看能不能整除
int i = 2;
while (i <= number) { //如果i大于number就退出循环
//能整除,则i为一个因数,number为整除的结果再继续从2开始除
if (number % i == 0) {
result.add(i);
number = number / i;
i = 2;
} else {
i++; //否则i++
}
}
return result;
}
}
- 启动Demo
下载已经打包好的arthas-demo.jar
java
curl -O https://alibaba.github.io/arthas/arthas-demo.jar
## 也可以浏览器直接访问这个链接,将jar包下载下来
https://arthas.aliyun.com/arthas-demo.jar
在命令行下执行
java -jar arthas-demo.jar
效果
- 启动arthas
1.因为arthas-demo.jar进程打开了一个窗口,所以另开一个命令窗口执行arthas-boot.jar
2.选择要粘附的进程:arthas-demo.jar
3.如果粘附成功,在arthas-demo.jar那个窗口中会出现日志记录的信息,记录在c:\Users\Administrator\logs目录下
4.如果端口号被占用,也可以通过以下命令换成另一个端口号执行
java
java -jar arthas-boot.jar --telnet-port 9998 --http-port -1
4. 通过浏览器连接arthas
Arthas目前支持Web Console,用户在attach成功之后,可以直接访问:http://127.0.0.1:3658/。 可以填入IP,远程连接其它机器上的arthas。
默认情况下,arthas只listen 127.0.0.1,所以如果想从远程连接,则可以使用 --target-ip参数指定listen的IP
小结
1.启动被诊断进程
2.启动arthas-boot.jar,粘贴上面的进程
3.不但可以通过命令行的方式来操作arthas也可以通过浏览器来访问arthas
五、快速入门:常用命令接触
5.1. dashboard仪表板
输入dashboard(仪表板),按回车/enter,会展示当前进程的信息,按ctrl+c可以中断执行。
注:输入前面部分字母,按tab可以自动补全命令
1.第一部分是显示JVM中运行的所有线程:所在线程组,优先级,线程的状态,CPU的占用率,是否是后台进程等
2.第二部分显示的JVM内存的使用情况
3.第三部分是操作系统的一些信息和Java版本号
5.2. 通过thread命令来获取到arthas-demo进程的Main Class
获取到arthas-demo进程的Main Class thread 1会打印线程ID 1的栈,通常是main函数的线程。
5.3. 通过jad来反编译Main Class
java
jad demo.MathGame
5.4. watch监视
通过watch命令来查看demo.MathGame#primeFactors函数的返回值: watch demo.MathGame primeFactors returnObj
5.5. 退出arthas
如果只是退出当前的连接,可以用quit或者exit命令。Attach到目标进程上的arthas还会继续运行,端口会保持开放,下次连接时可以直接连接上。 如果想完全退出arthas,可以执行stop命令。
5.6 小结
1.如何启动arthas?
java
java -jar arthas-boot.jar
2.说说以下命令的作用
命令功能dashboard显示JVM中内存的情况,JVM中环境信息thread显示当前进程所有线程信息jad反编译指定的类或方法watch监视某个方法的执行情况,监视了返回值quit,exit, stop退出或停止arthas
六、基础命令之一
6.1 help
作用:查看命令帮助信息
6.2 cat
作用:打印文件内容,和linux里的cat命令类似,如果没有写路径,则显示当前目录下的文件
6.3 grep
作用:匹配查找,和linux里的grep命令类似,但它只能用于管道命令
举例:只显示包含java字符串的行系统属性
cmd
sysprop | grep java
显示包含java字符串的行和行号的系统属性
cmd
sysprop | grep java -n
显示包含system字符串的10行信息
cmd
thread | grep system -m 10
使用正则表达式,显示包含2个o字符的线程信息
cmd
thread | grep -e "o+"
6.4 pwd
作用:返回当前的工作目录,和linux命令类似
pwd: Print Work Directory 打印当前工作目录
6.5 cls
作用:清空当前屏幕区域
七、基础命令之二
7.1 session
作用:查看当前会话的信息
7.2 reset
作用:重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
txt
还原指定类
reset Test
还原所有以List结尾的类
reset *List
还原所有的类
reset
7.3 version
作用:输出当前目标 Java 进程所加载的 Arthas 版本号
7.4 history
作用:打印命令历史
7.5 quit
作用:退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
7.6 stop
作用:关闭 Arthas 服务端,所有 Arthas 客户端全部退出
7.7 keymap
作用:Arthas快捷键列表及自定义快捷键
7.8 Arthas 命令行快捷键
-
任何时候 tab 键,会根据当前的输入给出提示
-
命令后敲 - 或 -- ,然后按 tab 键,可以展示出此命令具体的选项
-
ctrl + c: 终止当前命令
-
ctrl + z: 挂起当前命令,后续可以 bg/fg 重新支持此命令,或 kill 掉
-
ctrl + a: 回到行首
-
ctrl + e: 回到行尾
小结
命令说明session显示当前会话的信息:进程的ID,会话IDreset重置类的增强,服务器关闭的时候会自动重置所有的类version显示arthas版本号quit退出当前会话,不会影响其它的会话stop退出arthas服务器,所有的会话都停止keymap获取快捷键
八、jvm相关命令之一
8.1 dashboard
作用:显示当前系统的实时数据面板,按q或ctrl+c退出
数据说明
- ID: Java级别的线程ID,注意这个ID不能跟jstack中的nativeID一一对应
- NAME: 线程名
- GROUP: 线程组名
- PRIORITY: 线程优先级, 1~10之间的数字,越大表示优先级越高
- STATE: 线程的状态
- CPU%: 线程消耗的cpu占比,采样100ms,将所有线程在这100ms内的cpu使用量求和,再算出每个线程的cpu使用占比。
- TIME: 线程运行总时间,数据格式为分:秒
- INTERRUPTED: 线程当前的中断位状态
- DAEMON: 是否是daemon线程
8.2 thread线程相关
作用:查看当前 JVM 的线程堆栈信息
举例:展示当前最忙的前3个线程并打印堆栈
cmd
thread -n 3
txt
当没有参数时,显示所有线程的信息
thread
显示1号线程的运行堆栈
thread 1
找出当前阻塞其他线程的线程,有时候我们发现应用卡住了, 通常是由于某个线程拿住了某个锁, 并且其他线程都在等待这把锁造成的。 为了排查这类问题, arthas提供了thread -b, 一键找出那个罪魁祸首。 thread -b
txt
指定采样时间间隔,每过1000毫秒采样,显示最占时间的3个线程
thread -i 1000 -n 3
txt
查看处于等待状态的线程
thread --state WAITING
8.3 jvm
作用:查看当前 JVM 的信息
THREAD相关
- COUNT: JVM当前活跃的线程数
- DAEMON-COUNT: JVM当前活跃的守护线程数
- PEAK-COUNT: 从JVM启动开始曾经活着的最大线程数
- STARTED-COUNT: 从JVM启动开始总共启动过的线程次数
- DEADLOCK-COUNT: JVM当前死锁的线程数
文件描述符相关
- MAX-FILE-DESCRIPTOR-COUNT:JVM进程最大可以打开的文件描述符数
- OPEN-FILE-DESCRIPTOR-COUNT:JVM当前打开的文件描述符数
8.4 sysprop
作用:查看和修改JVM的系统属性
举例:
txt
查看所有属性
sysprop
查看单个属性,支持通过tab补全
sysprop java.version
txt
修改单个属性
sysprop user.country
user.country=US
sysprop user.country CN
Successfully changed the system property.
user.country=CN
九、jvm相关命令之二
9.1 sysenv
作用:查看当前JVM的环境属性(System Environment Variables)
举例
txt
查看所有环境变量
sysenv
查看单个环境变量
sysenv USER
9.2 vmoption
作用:查看,更新VM诊断相关的参数
举例
txt
查看所有的选项
vmoption
查看指定的选项
vmoption PrintGCDetails
txt
更新指定的选项
vmoption PrintGCDetails true
9.3 getstatic
作用:通过getstatic命令可以方便的查看类的静态属性
txt
语法
getstatic 类名 属性名
举例
显示demo.MathGame类中静态属性random
getstatic demo.MathGame random
9.4 ognl
作用:执行ognl表达式,这是从3.0.5版本新增的功能
txt
OGNL语法
http://commons.apache.org/proper/commons-ognl/language-guide.htmlja
txt
举例
调用静态函数
ognl '@[email protected]("hello")'
获取静态类的静态字段
ognl '@demo.MathGame@random'
执行多行表达式,赋值给临时变量,返回一个List
ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
十、class/classloader相关命令之一
10.1 sc
作用:查看JVM已加载的类信息,"Search-Class" 的简写,这个命令能搜索出所有已经加载到 JVM 中的 Class 信息
sc 默认开启了子类匹配功能,也就是说所有当前类的子类也会被搜索出来,想要精确的匹配,请打开options disable-sub-class true开关
txt
举例
模糊搜索,demo包下所有的类
sc demo.*
打印类的详细信息
sc -d demo.MathGame
txt
打印出类的Field信息
sc -df demo.MathGame
10.2 sm
作用:查看已加载类的方法信息 "Search-Method" 的简写,这个命令能搜索出所有已经加载了 Class 信息的方法信息。
sm 命令只能看到由当前类所声明 (declaring) 的方法,父类则无法看到。
txt
举例
显示String类加载的方法
sm java.lang.String
txt
显示String中的toString方法详细信息
sm -d java.lang.String toString
十一、class/classloader相关命令之二
11.1 jad
作用:反编译指定已加载类源码 jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于你理解业务逻辑;在 Arthas Console 上,反编译出来的源码是带语法高亮的,阅读更方便当然,反编译出来的 java 代码可能会存在语法错误,但不影响你进行阅读理解
txt
举例
编译java.lang.String
jad java.lang.String
反编绎时只显示源代码,默认情况下,反编译结果里会带有ClassLoader信息,通过--source-only选项,可以只打印源代码。方便和mc/redefine命令结合使用。
jad --source-only demo.MathGame
txt
反编译指定的函数
jad demo.MathGame main
11.2 mc
作用:Memory Compiler/内存编译器,编译.java文件生成.class
txt
举例
在内存中编译Hello.java为Hello.class
mc /root/Hello.java
可以通过-d命令指定输出目录
mc -d /root/bbb /root/Hello.java
11.3 redefine
作用:加载外部的.class文件,redefine到JVM里 注意, redefine后的原来的类不能恢复,redefine有可能失败(比如增加了新的field)。 reset命令对redefine的类无效。如果想重置,需要redefine原始的字节码。 redefine命令和jad/watch/trace/monitor/tt等命令会冲突。执行完redefine之后,如果再执行上面提到的命令,则会把redefine的字节码重置。
redefine的限制
- 不允许新增加field/method
- 正在跑的函数,没有退出不能生效,比如下面新增加的
System.out.println,只有run()函数里的会生效
java
public class MathGame {
public static void main(String[] args) throws InterruptedException {
MathGame game = new MathGame();
while (true) {
game.run();
TimeUnit.SECONDS.sleep(1);
// 这个不生效,因为代码一直跑在 while里
System.out.println("in loop");
}
}
public void run() throws InterruptedException {
// 这个生效,因为run()函数每次都可以完整结束
System.out.println("call run()");
try {
int number = random.nextInt();
List<Integer> primeFactors = primeFactors(number);
print(number, primeFactors);
} catch (Exception e) {
System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage());
}
}
}
txt
案例:结合 jad/mc 命令使用
步骤
1. 使用jad反编译demo.MathGame输出到/root/MathGame.java
jad --source-only demo.MathGame > /root/MathGame.java
2.按上面的代码编辑完毕以后,使用mc内存中对新的代码编译
mc /root/MathGame.java -d /root
3.使用redefine命令加载新的字节码
redefine /root/demo/MathGame.class