Java 线上问题定位处理利器:Arthas , 附使用案例

程序员考拉

共 2388字,需浏览 5分钟

 · 2020-11-24

公众号关注 “GitHub今日热榜
设为 “星标”,带你挖掘更多开发神器!





2018年9月 Alibaba 开源了一款 Java 诊断工具 Arthas , Arthas 支持 JDK6+,采用命令行交互模式,提供 Tab 自动补全功能,可以方便地定位和诊断线上程序运行问题。


Arthas 官网有详细的使用文档说明,同时 GitHub 开源的 Arthas 项目 Issues 里有网友的问题反馈,还有大量的使用案例,可以作为一个交流的学习平台使用。 贴上 GitHub 项目地址:https://github.com/alibaba/arthas 。




1

Arthas 使用场景



Arthas 功能强大且丰富,能做的事情很多,下面列举几个常见的使用场景。


  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?

  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

  5. 是否有一个全局视角来查看系统的运行状况?

  6. 有什么办法可以监控到JVM的实时运行状态?

  7. 怎么快速定位应用的热点,生成火焰图?

  8. 为什么 CPU 又升高了,到底是哪里占用了 CPU ?

  9. 运行的多线程有死锁吗?有阻塞吗?




2

Arthas 在线教程使用方法



1、首先访问在线教程:https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn ,从菜单中选择你想要学习的课程: 



2、课程介绍页面会标明课程的难度和需要的时间,帮助你了解该课程的基本信息。点击 START SCENARIO 开始学习。 



3、进入课程,左侧是该步骤说明,右侧是一个已经准备好的终端,直接可以使用。点击左侧黑块部分就可以在右侧执行:



4、点击右侧标签可以切换终端。之后就是跟着步骤说明,一步步的完成学习即可:





3

Arthas 快速入门



arthas-demo 源代码:


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();
        while (true) {
            game.run();
            TimeUnit.SECONDS.sleep(1);
        }
    }

    public void run() throws InterruptedException {
        try {
            int number = random.nextInt()/10000;
            List 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 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 primeFactors(int number) {
        if (number < 2) {
            illegalArgumentCount++;
            throw new IllegalArgumentException("number is: " + number + ", need >= 2");
        }

        List result = new ArrayList();
        int i = 2;
        while (i <= number) {
            if (number % i == 0) {
                result.add(i);
                number = number / i;
                i = 2;
            } else {
                i++;
            }
        }

        return result;
    }
}


arthas-demo是一个简单的程序,每隔一秒生成一个随机数,再执行质因数分解,并打印出分解结果。


1、启动 Demo


curl -O https://arthas.aliyun.com/arthas-demo.jar
java -jar arthas-demo.jar


2、启动 arthas


在命令行下面执行(使用和目标进程一致的用户启动,否则可能attach失败):


curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar


  • 执行该程序的用户需要和目标进程具有相同的权限。比如以admin用户来执行:sudo su admin && java -jar arthas-boot.jarsudo -u admin -EH java -jar arthas-boot.jar

  • 如果attach不上目标进程,可以查看~/logs/arthas/ 目录下的日志。

  • 如果下载速度比较慢,可以使用aliyun的镜像:java -jar arthas-boot.jar --repo-mirror aliyun --use-http

  • java -jar arthas-boot.jar -h 打印更多参数信息。


选择应用java进程:


$ $ java -jar arthas-boot.jar
* [1]: 35542
  [2]: 71560 arthas-demo.jar


Demo进程是第2个,则输入2,再输入回车/enter。Arthas会attach到目标进程上,并输出日志:


[INFO] Try to attach process 71560
[INFO] Attach process 71560 success.
[INFO] arthas-client connect 127.0.0.1 3658

  ,---. ,------. ,--------.,--. ,--. ,---. ,---.
 / O \ | .--. ''--. .--'| '--' | / O \ '   .-'
| .-. || '
--'.'   | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--'
 `--'`--' '--' `--'   `--'  `--'`--' `--'`-----'

wiki: https://arthas.aliyun.com/doc
version: 3.0.5.20181127201536
pid: 71560
time: 2018-11-28 19:16:24
$


3、查看dashboard


输入dashboard,按回车/enter,会展示当前进程的信息,按ctrl+c可以中断执行。


$ dashboard
ID NAME GROUP PRIORI STATE %CPU TIME INTERRU DAEMON
17 pool-2-thread-1 system 5 WAITIN 67 0:0 false false
27 Timer-for-arthas-dashb system 10 RUNNAB 32 0:0 false true
11 AsyncAppender-Worker-a system 9 WAITIN 0 0:0 false true
9 Attach Listener system 9 RUNNAB 0 0:0 false true
3 Finalizer system 8 WAITIN 0 0:0 false true
2 Reference Handler      system         10     WAITIN 0       0:0    false   true
4      Signal Dispatcher system         9      RUNNAB 0       0:0    false   true
26     as-command-execute-dae system         10     TIMED_ 0       0:0    false   true
13     job-timeout            system         9      TIMED_ 0       0:0    false   true
1      main                   main           5      TIMED_ 0       0:0    false   false
14     nioEventLoopGroup-2-1  system         10     RUNNAB 0       0:0    false   false
18     nioEventLoopGroup-2-2  system         10     RUNNAB 0       0:0    false   false
23     nioEventLoopGroup-2-3  system         10     RUNNAB 0       0:0    false   false
15     nioEventLoopGroup-3-1  system         10     RUNNAB 0       0:0    false   false
Memory             used total max    usage GC
heap               32M 155M 1820M 1.77% gc.ps_scavenge.count 4
ps_eden_space 14M 65M 672M 2.21% gc.ps_scavenge.time(m 166
ps_survivor_space 4M 5M 5M s)
ps_old_gen 12M 85M 1365M 0.91% gc.ps_marksweep.count 0
nonheap 20M 23M -1           gc.ps_marksweep.time( 0
code_cache 3M 5M 240M 1.32% ms)
Runtime
os.name Mac OS X
os.version 10.13.4
java.version 1.8.0_162
java.home /Library/Java/JavaVir
                       tualMachines/jdk1.8.0
                       _162.jdk/Contents/Hom
                       e/jre


4、通过thread命令来获取到arthas-demo进程的Main Class。


thread 1会打印线程ID 1的栈,通常是main函数的线程。


$ thread 1 | grep 'main('
    at demo.MathGame.main(MathGame.java:17)


5、通过jad来反编译Main Class


$ jad demo.MathGame
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@3d4eac69
  +-sun.misc.Launcher$ExtClassLoader@66350f69
Location:
/tmp/arthas-demo.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 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 primeFactors) {
        StringBuffer sb = new StringBuffer("" + number + "=");
        Iterator 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 primeFactors(int number) {
        if (number < 2) {
            ++this.illegalArgumentCount;
            throw new IllegalArgumentException("number is: " + number + ", need >= 2");
        }
        ArrayList result = new ArrayList();
        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.


6、watch


通过watch命令来查看demo.MathGame#primeFactors函数的返回值:


$ watch demo.MathGame primeFactors returnObj
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 107 ms.
ts=2018-11-28 19:22:30; [cost=1.715367ms] result=null
ts=2018-11-28 19:22:31; [cost=0.185203ms] result=null
ts=2018-11-28 19:22:32; [cost=19.012416ms] result=@ArrayList[
    @Integer[5],
    @Integer[47],
    @Integer[2675531],
]
ts=2018-11-28 19:22:33; [cost=0.311395ms] result=@ArrayList[
    @Integer[2],
    @Integer[5],
    @Integer[317],
    @Integer[503],
    @Integer[887],
]
ts=2018-11-28 19:22:34; [cost=10.136007ms] result=@ArrayList[
    @Integer[2],
    @Integer[2],
    @Integer[3],
    @Integer[3],
    @Integer[31],
    @Integer[717593],
]

ts=2018-11-28 19:22:35; [cost=29.969732ms] result=@ArrayList[
    @Integer[5],
    @Integer[29],
    @Integer[7651739],
]


7、退出arthas


如果只是退出当前的连接,可以用quit或者exit命令。Attach到目标进程上的arthas还会继续运行,端口会保持开放,下次连接时可以直接连接上。


如果想完全退出arthas,可以执行stop命令。


GitHub开源地址:https://github.com/alibaba/arthas








关注GitHub今日热榜,专注挖掘好用的开发工具,致力于分享优质高效的工具、资源、插件等,助力开发者成长!







点个在看 你最好看




浏览 98
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报