时间: 2020-09-03 00:08:26 人气: 2304 评论: 0
在使用 Arthas 之前,当遇到 Java 线上问题时,如 CPU 飙升、负载突高、内存溢出等问题,你需要查命令,查网络,然后 jps、jstack、jmap、jhat、jstat、hprof 等一通操作。最终焦头烂额,还不一定能查出问题所在。而现在,大多数的常见问题你都可以使用 Arthas 轻松定位,迅速解决,及时止损,准时下班。
Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具。支持 JDK6+, 采用命令行交互模式,提供 Tab 自动不全,可以方便的定位和诊断线上程序运行问题。截至本篇文章编写时,已经收获 Star 17000+。
Arthas 官方文档十分详细,本文也参考了官方文档内容,同时在开源在的 Github 的项目里的 Issues 里不仅有问题反馈,更有大量的使用案例,也可以进行学习参考。
开源地址:https://github.com/alibaba/arthas
官方文档:https://alibaba.github.io/arthas
得益于 Arthas 强大且丰富的功能,让 Arthas 能做的事情超乎想象。下面仅仅列举几项常见的使用情况,更多的使用场景可以在熟悉了 Arthas 之后自行探索。
前文已经提到,Arthas 是一款命令行交互模式的 Java 诊断工具,由于是 Java 编写,所以可以直接下载相应 的 jar 包运行。
可以在官方 Github 上进行下载,如果速度较慢,可以尝试国内的码云 Gitee 下载。
# github下载 wget https://alibaba.github.io/arthas/arthas-boot.jar # 或者 Gitee 下载 wget https://arthas.gitee.io/arthas-boot.jar # 打印帮助信息 java -jar arthas-boot.jar -h
Arthas 只是一个 java 程序,所以可以直接用 java -jar 运行。运行时或者运行之后要选择要监测的 Java 进程。
# 运行方式1,先运行,在选择 Java 进程 PID java -jar arthas-boot.jar # 选择进程(输入[]内编号(不是PID)回车) [INFO] arthas-boot version: 3.1.4 [INFO] Found existing java process, please choose one and hit RETURN. * [1]: 11616 com.Arthas [2]: 8676 [3]: 16200 org.jetbrains.jps.cmdline.Launcher [4]: 21032 org.jetbrains.idea.maven.server.RemoteMavenServer # 运行方式2,运行时选择 Java 进程 PID java -jar arthas-boot.jar [PID]
查看 PID 的方式可以通过 ps 命令,也可以通过 JDK 提供的 jps命令。
# 查看运行的 java 进程信息 $ jps -mlvV # 筛选 java 进程信息 $ jps -mlvV | grep [xxx]
jps 筛选想要的进程方式。
在出现 Arthas Logo 之后就可以使用命令进行问题诊断了。下面会详细介绍。
更多的启动方式可以参考 help 帮助命令。
# 其他用法 EXAMPLES: java -jar arthas-boot.jar <pid> java -jar arthas-boot.jar --target-ip 0.0.0.0 java -jar arthas-boot.jar --telnet-port 9999 --http-port -1 java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws' java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws' --agent-id bvDOe8XbTM2pQWjF4cfw java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat' java -jar arthas-boot.jar -c 'sysprop; thread' <pid> java -jar arthas-boot.jar -f batch.as <pid> java -jar arthas-boot.jar --use-version 3.1.4 java -jar arthas-boot.jar --versions java -jar arthas-boot.jar --session-timeout 3600 java -jar arthas-boot.jar --attach-only java -jar arthas-boot.jar --repo-mirror aliyun --use-http
Arthas 目前支持 Web Console,在成功启动连接进程之后就已经自动启动,可以直接访问 http://127.0.0.1:8563/ 访问,页面上的操作模式和控制台完全一样。
3.4 常用命令
下面列举一些 Arthas 的常用命令,看到这里你可能还不知道怎么使用,别急,后面会一一介绍。
命令 | 介绍 |
|---|---|
dashboard | 当前系统的实时数据面板 |
thread | 查看当前 JVM 的线程堆栈信息 |
watch | 方法执行数据观测 |
trace | 方法内部调用路径,并输出方法路径上的每个节点上耗时 |
stack | 输出当前方法被调用的调用路径 |
tt | 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测 |
monitor | 方法执行监控 |
jvm | 查看当前 JVM 信息 |
vmoption | 查看,更新 JVM 诊断相关的参数 |
sc | 查看 JVM 已加载的类信息 |
sm | 查看已加载类的方法信息 |
jad | 反编译指定已加载类的源码 |
classloader | 查看 classloader 的继承树,urls,类加载信息 |
heapdump | 类似 jmap 命令的 heap dump 功能 |
使用 shutdown 退出时 Arthas 同时自动重置所有增强过的类 。
上面已经了解了什么是 Arthas,以及 Arthas 的启动方式,下面会依据一些情况,详细说一说 Arthas 的使用方式。在使用命令的过程中如果有问题,每个命令都可以是 -h 查看帮助信息。
首先编写一个有各种情况的测试类运行起来,再使用 Arthas 进行问题定位,
import java.util.HashSet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import lombok.extern.slf4j.Slf4j; /** * <p> * Arthas Demo * 公众号:未读代码 * * @Author niujinpeng */ @Slf4j public class Arthas { private static HashSet hashSet = new HashSet(); /** 线程池,大小1*/ private static ExecutorService executorService = Executors.newFixedThreadPool(1); public static void main(String[] args) { // 模拟 CPU 过高,这里注释掉了,测试时可以打开 // cpu(); // 模拟线程阻塞 thread(); // 模拟线程死锁 deadThread(); // 不断的向 hashSet 集合增加数据 addHashSetThread(); } /** * 不断的向 hashSet 集合添加数据 */ public static void addHashSetThread() { // 初始化常量 new Thread(() -> { int count = 0; while (true) { try { hashSet.add("count" + count); Thread.sleep(10000); count++; } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } public static void cpu() { cpuHigh(); cpuNormal(); } /** * 极度消耗CPU的线程 */ private static void cpuHigh() { Thread thread = new Thread(() -> { while (true) { log.info("cpu start 100"); } }); // 添加到线程 executorService.submit(thread); } /** * 普通消耗CPU的线程 */ private static void cpuNormal() { for (int i = 0; i < 10; i++) { new Thread(() -> { while (true) { log.info("cpu start"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } /** * 模拟线程阻塞,向已经满了的线程池提交线程 */ private static void thread() { Thread thread = new Thread(() -> { while (true) { log.debug("thread start"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); // 添加到线程 executorService.submit(thread); } /** * 死锁 */ private static void deadThread() { /** 创建资源 */ Object resourceA = new Object(); Object resourceB = new Object(); // 创建线程 Thread threadA = new Thread(() -> { synchronized (resourceA) { log.info(Thread.currentThread() + " get ResourceA"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } log.info(Thread.currentThread() + "waiting get resourceB"); synchronized (resourceB) { log.info(Thread.currentThread( 技术沙龙 教程文章 热点综合