Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。
通常,本地开发环境无法访问生产环境。如果在生产环境中遇到问题,则无法使用 IDE 远程调试。更糟糕的是,在生产环境中调试是不可接受的,因为它会暂停所有线程,导致服务暂停。
开发人员可以尝试在测试环境或者预发环境中复现生产环境中的问题。但是,某些问题无法在不同的环境中轻松复现,甚至在重新启动后就消失了。
如果您正在考虑在代码中添加一些日志以帮助解决问题,您将必须经历以下阶段:测试、预发,然后生产。这种方法效率低下,更糟糕的是,该问题可能无法解决,因为一旦 JVM 重新启动,它可能无法复现,如上文所述。
Arthas 旨在解决这些问题,开发人员可以在线解决生产问题,无需 JVM 重启,无需代码更改。Arthas 作为观察者永远不会暂停正在运行的线程。
Arthas
是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。
当你遇到以下类似问题而束手无策时,Arthas
可以帮助你解决:
Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。
# 登录linux 服务器,执行下载math-game.jar并运行math-game程序
curl -O https://arthas.aliyun.com/math-game.jar
java -jar math-game.jar
math-game
是一个简单的程序,每隔一秒生成一个随机数,再执行质因数分解,并打印出分解结果。
math-game
源代码: https://github.com/alibaba/arthas/blob/master/math-game/src/main/java/demo/MathGame.java
在命令行下面执行(使用和目标进程一致的用户启动,否则可能 attach 失败)
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
admin
用户来执行:sudo su admin && java -jar arthas-boot.jar
或 sudo -u admin -EH java -jar arthas-boot.jar
。~/logs/arthas/
目录下的日志。java -jar arthas-boot.jar --repo-mirror aliyun --use-http
java -jar arthas-boot.jar -h
打印更多参数信息。启动 arthas-boot
程序后控制台会出现了如下日志信息:
[root@iZf8zdh51orpja1xwy1mutZ local]# java -jar arthas-boot.jar
[INFO] JAVA_HOME: /usr/local/jdk1.8.0_381/jre
[INFO] arthas-boot version: 3.7.2
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 445171 /usr/local/nacos/target/nacos-server.jar
[2]: 436414 math-game.jar
进程 1 是nacos
, math-game
进程是第 2 个,则输入 2,再输入回车/enter
。Arthas 会 attach 到目标进程上,并输出日志:
选择应用 java 进程:
[INFO] Start download arthas from remote server: https://arthas.aliyun.com/download/3.7.2?mirror=aliyun
[INFO] Download arthas success.
[INFO] arthas home: /root/.arthas/lib/3.7.2/arthas
[INFO] Try to attach process 436414
[INFO] Attach process 436414 success.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
wiki https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version 3.7.2
main_class
pid 436414
time 2024-04-12 00:04:18
输入dashboard,按回车/enter
,会展示当前进程的信息,按ctrl+c
可以中断执行。
[arthas@436414]$ dashboard
ID NAME GROUP PRIORITY STATE %CPU DELTA_TI TIME INTERRUP DAEMON
-1 C2 CompilerThread0 - -1 - 0.0 0.000 0:0.331 false true
-1 C1 CompilerThread1 - -1 - 0.0 0.000 0:0.299 false true
1 main main 5 TIMED_WA 0.0 0.000 0:0.139 false false
-1 VM Periodic Task Thread - -1 - 0.0 0.000 0:0.112 false true
19 arthas-NettyHttpTelnetBootst system 5 RUNNABLE 0.0 0.000 0:0.062 false true
-1 GC task thread#0 (ParallelGC - -1 - 0.0 0.000 0:0.046 false true
-1 GC task thread#1 (ParallelGC - -1 - 0.0 0.000 0:0.038 false true
-1 VM Thread - -1 - 0.0 0.000 0:0.032 false true
13 arthas-NettyHttpTelnetBootst system 5 RUNNABLE 0.0 0.000 0:0.017 false true
8 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.012 false true
Memory used total max usage GC
heap 20M 72M 796M 2.53% gc.ps_scavenge.count 5
ps_eden_space 1M 29M 286M 0.69% gc.ps_scavenge.time(ms) 33
ps_survivor_space 0K 2048K 2048K 0.00% gc.ps_marksweep.count 1
ps_old_gen 18M 41M 597M 3.05% gc.ps_marksweep.time(ms) 38
nonheap 27M 28M -1 97.10%
code_cache 4M 4M 240M 1.88%
metaspace 20M 21M -1 96.67%
Runtime
os.name Linux
os.version 5.10.134-16.1.al8.x86_64
[arthas@629401]$ thread
Threads Total: 21, NEW: 0, RUNNABLE: 7, BLOCKED: 0, WAITING: 3, TIMED_WAITING: 4, TERMINATED: 0, Internal threads:
7
ID NAME GROUP PRIORITY STATE %CPU DELTA_TI TIME INTERRUP DAEMON
20 arthas-command-execute system 5 RUNNABLE 0.25 0.000 0:0.002 false true
12 Keep-Alive-Timer system 8 TIMED_WA 0.08 0.000 0:0.000 false true
-1 VM Thread - -1 - 0.05 0.000 0:0.025 false true
-1 VM Periodic Task Thread - -1 - 0.03 0.000 0:0.014 false true
2 Reference Handler system 10 WAITING 0.0 0.000 0:0.001 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.002 false true
4 Signal Dispatcher system 9 RUNNABLE 0.0 0.000 0:0.000 false true
8 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.011 false true
10 arthas-timer system 9 WAITING 0.0 0.000 0:0.000 false true
13 arthas-NettyHttpTelnetBootst system 5 RUNNABLE 0.0 0.000 0:0.018 false true
14 arthas-NettyWebsocketTtyBoot system 5 RUNNABLE 0.0 0.000 0:0.001 false true
15 arthas-NettyWebsocketTtyBoot system 5 RUNNABLE 0.0 0.000 0:0.001 false true
16 arthas-shell-server system 9 TIMED_WA 0.0 0.000 0:0.000 false true
17 arthas-session-manager system 9 TIMED_WA 0.0 0.000 0:0.000 false true
19 arthas-NettyHttpTelnetBootst system 5 RUNNABLE 0.0 0.000 0:0.055 false true
1 main main 5 TIMED_WA 0.0 0.000 0:0.066 false false
-1 GC task thread#1 (ParallelGC - -1 - 0.0 0.000 0:0.040 false true
-1 C1 CompilerThread1 - -1 - 0.0 0.000 0:0.268 false true
-1 GC task thread#0 (ParallelGC - -1 - 0.0 0.000 0:0.040 false true
-1 C2 CompilerThread0 - -1 - 0.0 0.000 0:0.326 false true
第1行表示总共有21个线程,其中:RUNNABLE 状态的线程7个、WAITING 状态的线程3个、TIMED_WAITING状态的线程4个,JVM内部线程7个。线程ID为-1的均为JVM内部线程。
math-game
进程的 Main Classthread 1
会打印线程 ID 1 的栈,通常是 main 函数的线程。
[arthas@533211]$ thread 1 | grep 'main('
at demo.MathGame.main(MathGame.java:17)
$ jad demo.MathGame
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@3d4eac69
+-sun.misc.Launcher$ExtClassLoader@66350f69
Location:
/tmp/math-game.jar
/*
* Decompiled with CFR 0_132.
*/
package demo;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
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();
do {
game.run();
TimeUnit.SECONDS.sleep(1L);
} while (true);
}
public void run() throws InterruptedException {
try {
int number = random.nextInt();
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 + "=");
Iterator<Integer> iterator = primeFactors.iterator();
while (iterator.hasNext()) {
int factor = iterator.next();
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;
}
}
Affect(row-cnt:1) cost in 970 ms.
如果只是退出当前的连接,可以用quit
或者exit
命令。Attach 到目标进程上的 arthas 还会继续运行,端口会保持开放,下次连接时可以直接连接上。
如果想完全退出 arthas,可以执行stop
命令。
.java
文件为.class
文件.class
文件,redefine 到 JVM 里.class
文件,retransform 到 JVM 里注意请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行 `stop` 或将增强过的类执行 `reset` 命令。
profiler: 使用async-profiler对应用采样,生成火焰图
jfr: 动态开启关闭JFR记录
当线上出现偶发的问题,比如需要 watch 某个条件,而这个条件一天可能才会出现一次时,异步后台任务就派上用场了,详情请参考:https://arthas.gitee.io/doc/async.html
>
将结果重写向到日志文件,使用 &
指定命令是后台运行,session 断开不影响任务执行(生命周期默认为 1 天)本文主要介绍了来自阿里的开源线上监控诊断工具 Arthas 的下载安装,并在Linux
服务器中使用Arthas对 Java应用进行了dashboard 查看控制面板数据 和 thread 命令查看监控进程中的线程堆栈信息,以及jad反编译命令的用法。然后简要介绍了Arthas 的命令列表,但是想要搞明白具体每个命令的用法和使用时的参数,请移步Arthas官网命令列表页 https://arthas.gitee.io/doc/commands.html
点击每个命令处的链接即可跳转到每个命令的用法详情页。