关于Arthas

Arthas 是 Alibaba在2018年9月开园的Java诊断工具。支持JDK6+ , 采用命令行交互模式,可以方面的定位和诊断线上程序问题。

官方文档:https://alibaba.github.io/arthas

安装 Arthas

# 从github下载Arthas
wget https://alibaba.github.io/arthas/arthas‐boot.jar
# 从gitee下载
wget https://arthas.gitee.io/arthas‐boot.jar

Arthas 是Java写的,要运行Arthas 只需要用java -jar 命令即可运行

java -jar arthas‐boot.jar

Arthas使用

示例代码,模拟线上问题

public class ArthasDemo {

    private static final HashSet<String> hashSet = new HashSet<>();

    public static void main(String[] args) {
        // 模拟 CPU 过高
        cpuHigh();
        // 模拟线程死锁
        deadThread();
        // 不断的向 hashSet 集合增加数据
        addHashSetThread();
    }

    private static void addHashSetThread() {

        new Thread(() -> {
            int count = 0;
            while (true) {
                count++;
                hashSet.add("count:" + count);

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }


    private static void cpuHigh() {
        new Thread(() -> {
            int a = 0;
            while (true) {
                a++;
            }
        }).start();
    }

    private static void deadThread() {
        Object lock_1 = new Object();
        Object lock_2 = new Object();

        Thread[] threads = {
                new Thread(() -> {
                    //尝试获取锁-1
                    synchronized (lock_1) {
                        System.out.println("thread -1 start");
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //尝试获取锁-2
                        synchronized (lock_2) {
                            System.out.println("thread -1 end");
                        }
                    }

                }),
                new Thread(() -> {
                    //尝试获取锁-2
                    synchronized (lock_2) {
                        System.out.println("thread -2 start");
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //尝试获取锁-1
                        synchronized (lock_1) {
                            System.out.println("thread -2 end");
                        }
                    }
                })
        };
        for (Thread thread : threads) {
            thread.start();
        }
    }
}

运行上述代码之后,使用Arthas进行分析

## 运行Arthas命令
[INFO] arthas-boot version: 3.3.7
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
## 以下输出的是Java的进程ID 和 进程信息  ArthasDemo这个是我的进程
* [1]: 33760 org.jetbrains.idea.maven.server.RemoteMavenServer36
  [2]: 33316 
  [3]: 34039 cn.pencilso.study.studyarthas.ArthasDemo
## 接下来输入序号,直接回车就可以进入这个进程信息,我这里的进程是序号为3的进程
3
## 回车后悔打印如下信息
[INFO] Start download arthas from remote server: https://maven.aliyun.com/repository/public/com/taobao/arthas/arthas-packaging/3.3.7/arthas-packaging-3.3.7-bin.zip
[INFO] File size: 11.39 MB, downloaded size: 10.02 MB, downloading ...
[INFO] Download arthas success.
[INFO] arthas home: /Users/pencilso/.arthas/lib/3.3.7/arthas
[INFO] Try to attach process 34039
[INFO] Attach process 34039 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.3.7                                                                 
pid       34039                                                                 
time      2020-07-28 20:51:26
## 接下来就会进入Arthas的命令行控制台,可以使用Arthas的功能了。

查看进程运行情况

在Arthas控制台输入dashboard命令,可以查看整个进程的运行情况,线程、内存、GC、运行环境信息 (而且会间隔刷新,可以按command+C 退出dashboard );

arthars_1

查看线程详细情况

在Arthas控制台输入thread命令,可以查看线程的详细情况 。

arthars_2

输入thread +id 可以查看线程对战

[arthas@34039]$ thread 19
"Thread-0" Id=19 RUNNABLE
    at cn.pencilso.study.studyarthas.ArthasDemo.lambda$cpuHigh$1(ArthasDemo.java:40)
    at cn.pencilso.study.studyarthas.ArthasDemo$$Lambda$1/1028214719.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

输入thread -b 可以查找出现死锁的线程 ,可以看到 id=20的"Thread-1" 和 id=21的"Thread-2"两个线程出现了死锁,”Thread-1“在ArthasDemo.java 的61行要获取的锁被其他线程持有,也就是被"Thread-2"所持有。

arthars_3

反编译类

这个场景主要是在我们不确定线上的代码是否是最新的情况下,通过Arthas命令行对类进行反编译,输出到控制台,以便我们观察代码是否是最新的。

输入jad +类的全名就可以反编译。

//执行反编译命令
[arthas@34039]$ jad cn.pencilso.study.studyarthas.ArthasDemo

ClassLoader:                                                                                                                                                         
+-sun.misc.Launcher$AppClassLoader@18b4aac2                                                                                                                          
  +-sun.misc.Launcher$ExtClassLoader@4f52751c                                                                                                                        

Location:                                                                                                                                                            
/Users/pencilso/Desktop/work-pase/study/study-arthas/target/classes/                                                                                                 

/*
 * Decompiled with CFR.
 */
package cn.pencilso.study.studyarthas;

import java.util.HashSet;

public class ArthasDemo {
    private static final HashSet<String> hashSet = new HashSet();

    public static void main(String[] args) {
        ArthasDemo.cpuHigh();
        ArthasDemo.deadThread();
        ArthasDemo.addHashSetThread();
    }
  
............. 以下省略

查看变量 & 修改变量

查看静态类的静态字段

##执行查看静态字段,ognl @className@FieldName
[arthas@34039]$ ognl '@cn.pencilso.study.studyarthas.ArthasDemo@hashSet'
@HashSet[
    @String[count:660],
    @String[count:1380],
    @String[count:420],
    @String[count:662],
    @String[count:1381],
    @String[count:661],
    @String[count:1140],
    .....

修改静态变量

## 调用清除集合方法
[arthas@34039]$ ognl '@cn.pencilso.study.studyarthas.ArthasDemo@hashSet.clear()'
null
## 添加一个元素到集合
[arthas@34039]$ ognl '@cn.pencilso.study.studyarthas.ArthasDemo@hashSet.add("ognl test")'
@Boolean[true]
## 再次查看集合里面的内容 (因为有一个线程在循环的加数据到这个hashset,所以有其他数据是正常的)
[arthas@34039]$ ognl '@cn.pencilso.study.studyarthas.ArthasDemo@hashSet'
@HashSet[
    @String[ognl test],
    @String[count:1780],
    @String[count:1781],
    @String[count:1782],
    @String[count:1772],
    @String[count:1773],
    @String[count:1774],
    @String[count:1775],
    @String[count:1771],
    @String[count:1776],
    @String[count:1777],
    @String[count:1778],
    @String[count:1779],
]
[arthas@34039]$ 

GC 日志分析

对于Java应用我们可以通过一些配置把程序运行过程中的GC日志全部打印出来,然后分析GC日志得到关键性指标,分析发生GC的原因,调优JVM参数。

‐Xloggc:./gc‐%t.log ‐XX:+PrintGCDetails ‐XX:+PrintGCDateStamps ‐XX:+PrintGCTimeStamps ‐XX:+PrintGCCause ‐XX:+UseGCLogFileRotation ‐XX:NumberOfGCLogFiles=10 ‐XX:GCLogFileSize=100M
  • Xloggc:输出日志文件 ./表示运行目录 %t表示时间
  • PrintGCDetails : 打印GC详情
  • PrintGCDateStamps:输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
  • PrintGCTimeStamps:输出GC的时间戳(以JVM启动到当期的总时长的时间戳形式)
  • PrintGCCause:输出GC原因
  • UseGCLogFileRotation:控制GClog文件滚动的开关
  • NumberOfGCLogFiles:控制GClog文件滚动的文件个数
  • GCLogFileSize:控制GClog文件达到多大时写入到下一个GClog文件,前提必须指定-Xloggc参数

开启GC日志分析必然会对线上服务产生一定的性能影响,但是总体来说还是影响很小的,就像在开发Java程序时我们经常会使用log4j输出一样,只是GC日志是C++代码去输出的日志。

如果GC一般都不会太频繁,如果太频繁的话,那就说明需要调优了。

我们可以通过第三方工具,对GC日志文件进行分析:https://gceasy.io/