Java线上问题定位神器-Arthas

当我们的Java程序上线之后,有时候会由于未能及时发现自己写的bug,导致程序抽风,比如:CPU、内存持续升高,堆栈溢出等,这时候我们可以借助JVM在线诊断分析工具–Arthas快速定位线上问题


        

Arthas(阿尔萨斯)是Alibaba开源的Java诊断工具,当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  • 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  • 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  • 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  • 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  • 是否有一个全局视角来查看系统的运行状况?
  • 有什么办法可以监控到JVM的实时运行状态?
  • 怎么快速定位应用的热点,生成火焰图?

Arthas支持JDK 6+,支持Linux/Mac/Winodws,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

下载Arthas

首先我们需要去下载Arthas,这里我下载的arthas-3.1.7-bin.zip,解压后的文件目录

在该压缩包中包含了一个demo程序用于上手练习,

常用命令

Arthas 基础命令:

  • help——查看命令帮助信息
  • cls——清空当前屏幕区域
  • session——查看当前会话的信息
  • reset——重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
  • version——输出当前目标 Java 进程所加载的 Arthas 版本号
  • history——打印命令历史
  • quit——退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
  • stop——关闭 Arthas 服务端,所有 Arthas 客户端全部退出
  • keymap——Arthas快捷键列表及自定义快捷键

dashboard

当前系统的实时数据面板,按 ctrl+c 退出。当运行在Ali-tomcat时,会显示当前tomcat的实时信息,如HTTP请求的qps, rt, 错误数, 线程池信息等等。

列名 含义
ID Java级别的线程ID,注意这个ID不能跟jstack中的nativeID一一对应
NAME 线程名
GROUP 线程组名
PRIORITY 线程优先级, 1~10之间的数字,越大表示优先级越高
STATE 线程的状态
%CPU 线程消耗的cpu占比,采样100ms,将所有线程在这100ms内的cpu使用量求和,再算出每个线程的cpu使用占比。
TIME 线程运行总时间,数据格式为分:秒
INTERRUPTED 线程当前的中断位状态
DAEMON 是否是daemon线程

thread

查看当前线程信息以及线程的堆栈

参数名称 参数说明
id 线程id
[n:] 指定最忙的前N个线程并打印堆栈
[b] 找出当前阻塞其他线程的线程
[i ] 指定cpu占比统计的采样间隔,单位为毫秒
  • thread:显示所有线程的信息

  • thread -n N:展示当前最忙的前N个线程并打印堆栈

  • thread 线程id: 显示指定线程的运行堆栈

  • thread -b: 找出当前阻塞其他线程的线程(有时候我们发现应用卡住了, 通常是由于某个线程拿住了某个锁, 并且其他线程都在等待这把锁造成的。 为了排查这类问题, arthas提供了thread -b, 一键找出那个罪魁祸首)

    注意, 目前只支持找出synchronized关键字阻塞住的线程, 如果是java.util.concurrent.Lock, 目前还不支持。

  • thread -i: 指定采样时间间隔(thread -n 3 -i 1000)

  • thread –state:查看指定状态的线程

sc

查看JVM已加载的类信息,是”Search-Class”的简写,这个命令能搜索出所有已经加载到 JVM 中的 Class 信息,这个命令支持的参数有 [d]、[E]、[f] 和 [x:]。

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
[d] 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。
如果一个类被多个ClassLoader所加载,则会出现多次
[E] 开启正则表达式匹配,默认为通配符匹配
[f] 输出当前类的成员变量信息(需要配合参数-d一起使用)
[x:] 指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出

class-pattern支持全限定名,如com.taobao.test.AAA,也支持com/taobao/test/AAA这样的格式,这样,我们从异常堆栈里面把类名拷贝过来的时候,不需要在手动把/替换为.啦。

sc 默认开启了子类匹配功能,也就是说所有当前类的子类也会被搜索出来,想要精确的匹配,请打开options disable-sub-class true开关

  • sc demo.*:模糊搜索
  • sc -d demo.MathGame:打印类的详细信息
  • sc -d -f demo.MathGame:打印出类的Field信息

jad

反编译指定已加载类的源码,jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于你理解业务逻辑

参数名称 参数说明
class-pattern 类名表达式匹配
[c:] 类所属 ClassLoader 的 hashcode
[E] 开启正则表达式匹配,默认为通配符匹配
  • jad 包名.类名:反编译指定类
  • jad –source-only 包名.类名:反编绎时只显示源代码(默认情况下,反编译结果里会带有ClassLoader信息)
  • jad 包名.类名 函数名:反编译指定的函数
  • jad -c 包名.类名:反编译时指定ClassLoader

mc

Memory Compiler/内存编译器,编译.java文件生成.class。

  • mc 类路径:编译.java文件生成.class
  • mc -c 类路径:可以通过-c参数指定classloader
  • mc -d [输出路径] 类路径:可以通过-d命令指定输出目录

注意,mc命令很大几率会失败。如果编译失败可以在本地编译好.class文件

redefine

加载外部的.class文件,redefine jvm已加载的类

注意, redefine后的原来的类不能恢复,redefine有可能失败(比如增加了新的field),参考jdk本身的文档。

  • redefine class文件路径:加载class到内存中
  • redefine -c class文件路径:指定classloader加载class到内存中

redefine的限制:

  • 不允许新增加field/method
  • 正在跑的函数,没有退出不能生效

快速入门


这个Jar包中只有MathGame.class,这里先启动java -jar arthas-demo.jar:

1
2
3
4
5
47086=2*13*1811
208=2*2*2*2*13
96301=23*53*79
illegalArgumentCount: 1, number is: -128441, need >= 2
illegalArgumentCount: 2, number is: -633, need >= 2

然后启动Arthas,并连接到相应的程序进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
E:\arthas-3.1.7-bin>java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.1.7
[INFO] Found existing java process, please choose one and hit RETURN.
* [1]: 11588 finalshell.jar
[2]: 8972 arthas-demo.jar
2
[INFO] arthas home: E:\arthas-3.1.7-bin
[INFO] Try to attach process 8972
[INFO] Found java home from System Env JAVA_HOME: D:\Program Files\Java\jdk1.8.0_231
[INFO] Attach process 8972 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'


wiki https://alibaba.github.io/arthas
tutorials https://alibaba.github.io/arthas/arthas-tutorials
version 3.1.7
pid 8972
time 2020-02-10 10:42:05

[arthas@8972]$

Arthas提供了dashboard命令来查看当前进程的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ID        NAME                          GROUP               PRIORITY STATE     %CPU      TIME      INTERRUPT DAEMON
12 AsyncAppender-Worker-arthas-c system 5 WAITING 0 0:0 false true
5 Attach Listener system 5 RUNNABLE 0 0:0 false true
3 Finalizer system 8 WAITING 0 0:0 false true
2 Reference Handler system 10 WAITING 0 0:0 false true
4 Signal Dispatcher system 9 RUNNABLE 0 0:0 false true
22 Timer-for-arthas-dashboard-ce system 10 RUNNABLE 0 0:0 false true
14 job-timeout system 5 TIMED_WAI 0 0:0 false true
1 main main 5 TIMED_WAI 0 0:0 false false
15 nioEventLoopGroup-2-1 system 10 RUNNABLE 0 0:0 false false
19 nioEventLoopGroup-2-2 system 10 RUNNABLE 0 0:0 false false
16 nioEventLoopGroup-3-1 system 10 RUNNABLE 0 0:0 false false
17 pool-1-thread-1 system 5 TIMED_WAI 0 0:0 false false
18 pool-2-thread-1 system 5 WAITING 0 0:0 false false
Memory used total max usage GC
heap 44M 145M 1694M 2.61% gc.ps_scavenge.count 4
ps_eden_space 26M 60M 625M 4.31% gc.ps_scavenge.time(ms) 29
ps_survivor_space 4M 5M 5M 99.38% gc.ps_marksweep.count 0
ps_old_gen 12M 80M 1271M 0.96% gc.ps_marksweep.time(ms) 0
nonheap 21M 22M -1 96.47%
code_cache 5M 5M 240M 2.37%
Runtime
os.name Windows 10
os.version 10.0
java.version 1.8.0_231
java.home D:\Program Files\Java\jre1.8.0_231
systemload.average -1.00
processors 8
uptime 2519s

我们先使用thread 1 | grep 'main('来查找main所在的类

1
2
[arthas@8972]$ thread 1 | grep 'main('
at demo.MathGame.main(MathGame.java:17)

我们先反编译看看类的的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
[arthas@8972]$ jad --source-only demo.MathGame
/*
* Decompiled with CFR.
*/
package demo;

import java.io.PrintStream;
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();
do {
game.run();
TimeUnit.SECONDS.sleep(1L);
} while (true);
}

public void run() throws InterruptedException {
try {
int number = random.nextInt() / 10000;
List<Integer> primeFactors = this.primeFactors(number);
MathGame.print(number, primeFactors);
}
catch (Exception e) {
System.out.println(String.format("illegalArgumentCount:%3d, ", this.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) {
++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;
}
}

这里我想在不停机的情况下修改一下判断条件,首先我先到处这个类的源代码:

1
jad --source-only demo.MathGame > D:/MathGame.java

修改一下逻辑:

1
2
3
4
if (number <=0) {
++this.illegalArgumentCount;
throw new IllegalArgumentException("number is: " + number + ", need >= 1");
}

修改好之后,使用javac.java编译成.class,我们使用redefine来加载类

1
2
[arthas@6780]$ redefine D:/MathGame.class
redefine success, size: 1

可以看到最终效果:

1
2
3
4
5
illegalArgumentCount:3284, number is: -118046, need >= 1
illegalArgumentCount:3285, number is: -16511, need >= 1
illegalArgumentCount:3286, number is: -166238, need >= 1
112722=2*3*18787
10250=2*5*5*5*41

原来的need >= 2变为了need >= 1
使用quit或者exit可以退出Arthas

这里只是一个简单的示例,其他实际例子未完待续….

本文内容来自arthas官网文档

Java线上问题定位神器-Arthas

https://blogs.52fx.biz/posts/4238163895.html

作者

eyiadmin

发布于

2020-02-09

更新于

2024-05-31

许可协议

评论