1.软件安装

curl -O https://arthas.aliyun.com/arthas-boot.jar
apt update
apt install -y default-jdk  
java -jar arthas-boot.jar 

离线包地址: https://maven.aliyun.com/repository/public/com/taobao/arthas/arthas-packaging/3.1.7/arthas -packaging-3.1.7-bin.zip

#如果下载速度慢 可执行如下命令,走阿里镜像
java -jar arthas-boot.jar --repo-mirror aliyun --use-http

2.快速入门:attach一个进程

0.目标:通过案例快速入门

  1. 执行一个jar包
  2. 通过arthas来attach粘附

1. 准备代码

以下是一个简单的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;
    }
}

2.编译执行

javac  MathGame.java
#注:有包名demo,得新建demo目录,并将字节码文件移入
mkdir -p demo && mv MathGame.class demo
java demo.MathGame

上面编译步骤也可以直接下载阿里示例

curl -O https://alibaba.github.io/arthas/arthas-demo.jar
# 在命令行下执行
java -jar arthas-demo.jar

接下来新建一个Shell窗口

3.运行arthas

## 获取ip的命令
hostname -I | awk -F " " '{print $2}'
## 全局监听,且更换端口
java -jar arthas-boot.jar --target-ip `hostname -I | awk -F " " '{print $2}'` --telnet-port 9998

将列举java进程 选择方括号中的序号1,回车 出现如下信息,并进入了arthas控制台 arthas-client connect 127.0.0.1 3658 Arthas目前支持Web Console,用户在attach成功之后,可以直接访问:http://127.0.0.1:3658/。 在Web版Arthas上可以填入IP,远程连接其它机器上的arthas。 默认情况下,arthas只listen 127.0.0.1,所以如果想从远程连接,则可以使用 –target-ip 参数指定 listen的IP

## 点击右上角隧道功能,输入端口9998,点击方法,即可访问web版,任然需要注意,在打开的隧道界面上,显示8563端口,证明,arthas服务起在8563上,所以需要将ip10-0-5-4-d28klgpubkjg01v0kta0-9998.ssh.skillup.host中的9998改成8563,再次填入IP输入框将8092再次填入,原来的Port(8563)输入框,输入dashboard(仪表板),按tab可以自动补全命令,按 回车/enter ,会展示当前进程的信息,按 ctrl+c 可以中断执行。

端口修改说明

1.仪表板

dashboard

2.线程命令

thread
#获取到arthas-demo进程的Main Class
thread 1 
#会打印线程ID 1的栈,通常是main函数的线程。

3.反编译

jad demo.MathGame

4.监视

watch demo.MathGame primeFactors returnObj

其他常用命令

#系统属性
sysprop
#JVM信息
jvm
#环境信息
sysenv
VM诊断相关的参数
vmoption
#指定采样时间间隔,每过1000毫秒采样,显示最占时间的3个线程
thread -i 1000 -n 3
#查看处于等待状态的线程
thread --state WAITING
#显示demo.MathGame类中静态属性random
getstatic demo.MathGame random

#调用静态函数
ognl '@java.lang.System@out.println("hello")'
#获取静态类的静态字段
ognl '@demo.MathGame@random'
#执行多行表达式,赋值给临时变量,返回一个List
ognl '#value1=@System@getProperty("java.home"),#value2=@System@getProperty("java.runtime.name"), {#value1, value2}'
#模糊搜索,demo包下所有的类
sc demo.*
#打印类的详细信息
sc -d demo.MathGame

#反编绎时只显示源代码,默认情况下,反编译结果里会带有#ClassLoader信息,通过--source-only选
#项,可以只打印源代码。方便和mc/redefine命令结合使用。
jad --source-only demo.MathGame
#反编译指定的函数
jad demo.MathGame main

#在内存中编译Hello.java为Hello.class
mc /root/Hello.java
#可以通过-d命令指定输出目录
mc -d /root/bbb /root/Hello.java
#结合 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

#把String类的字节码文件保存到~/logs/arthas/classdump/目录下
dump java.lang.String
#把demo包下所有的类的字节码文件保存到~/logs/arthas/classdump/目录下
dump demo.*
#过5秒统计一次,统计类demo.MathGame中primeFactors方法
monitor -c 5 demo.MathGame primeFactors
#观察demo.MathGame类中primeFactors方法出参和返回值,结果属性遍历深度为2。
params表示所有参数数组,returnObject表示返回值
watch demo.MathGame primeFactors "{params,returnObj}" -x 2
#trace函数指定类的指定方法
trace demo.MathGame run
#条件表达式来过滤,第0个参数的值小于0,-n表示获取2次
stack demo.MathGame primeFactors 'params[0]<0' -n 2
#据执行时间来过滤,耗时大于5毫秒
stack demo.MathGame primeFactors '#cost>5'

3.实践:哪个控制器处理了请求

步骤 1. trace定位DispatcherServlet

#在浏览器上进行登录操作,检查最耗时的方法
trace *.DispatcherServlet *
``
 2. jad反编译DispatcherServlet
```bash
#可以分步trace,请求最终是被ispatcherServlet#doDispatch()处理了
trace *.FrameworkServlet doService
  1. watch定位handler bash #trace结果里把调用的行号打印出来了,我们可以直接在IDE里查看代码(也可以用jad命令反编译) jad --source-only *.DispatcherServlet doDispatch 卸载arthas bash rm -rf ~/.arthas/ rm -rf ~/logs/arthas

4.五大问题定位场景

1.慢方法定位

# 1. 追踪方法内部调用路径  
trace com.example.OrderService getOrderById '#cost>1000' -n 5  

2.线程阻塞定位

传统方案

jstack > thread.log # 但阻塞已结束 

Arthas方案

# 1. 查看线程状态分布  
thread -b # 显示阻塞线程  

# 2. 监控锁竞争情况  
watch java.util.concurrent.locks.ReentrantLock getQueueLength 

3.内存泄漏定位

传统方案

jmap -histo:live pid # 触发Full GC破坏现场  

Arthas方案

# 1. 监控堆内存对象  
dashboard -i 5000 # 5秒刷新一次  

# 2. 追踪对象创建路径  
vmtool --action getInstances --className LoginDTO --limit 10  

4.热修复代码

# 1. 反编译问题方法  
jad com.example.UserService listUsers  

# 2. 修改本地文件  
vi UserService.java # 修复内存泄漏代码  

# 3. 热更新类  
redefine -c 327a3b4 /tmp/UserService.class 

5.调用监视

# 1. 监控方法入参/返回值  
watch com.service.OrderService updateStatus  
  "{params,returnObj}" -x 3  

# 2. 观察调用链路  
stack com.service.OrderService updateStatus  

5.arthas原理

Attach机制:通过VirtualMachine.attach注入Agent
字节码织入:利用ASM修改方法体添加监控逻辑
类隔离:自定义ClassLoader防止污染业务代码

6.arthas其他使用

# 1. 大屏概览  
dashboard -i 5000  

# 2. 定位CPU热点  
profiler start # 开始采样  
profiler stop --format html # 生成火焰图  

# 3. 追踪慢方法  
trace *StringUtils substring '#cost>100'