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

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> Java 线上问题定位处理利器——Arthas , 附使用案例

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

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

1

Arthas 使用场景

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

  • 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  • 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  • 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  • 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  • 是否有一个全局视角来查看系统的运行状况?
  • 有什么办法可以监控到JVM的实时运行状态?
  • 怎么快速定位应用的热点,生成火焰图?
  • 为什么 CPU 又升高了,到底是哪里占用了 CPU ?
  • 运行的多线程有死锁吗?有阻塞吗?
  • 2

    Arthas 在线教程使用方法

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

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

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

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

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

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

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

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

    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;
                ListInteger 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, ListInteger 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 ListInteger primeFactors(int number) {
            if (number  2) {
                illegalArgumentCount++;
                throw new IllegalArgumentException("number is: " + number + ", need = 2");
            }
    
            ListInteger result = new ArrayListInteger();
            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.jar` 或 `sudo -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();
                ListInteger 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, ListInteger primeFactors) {
            StringBuffer sb = new StringBuffer("" + number + "=");
            IteratorInteger 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 ListInteger primeFactors(int number) {
            if (number  2) {
                ++this.illegalArgumentCount;
                throw new IllegalArgumentException("number is: " + number + ", need = 2");
            }
            ArrayListInteger result = new ArrayListInteger();
            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今日热榜,专注挖掘好用的开发工具,致力于分享优质高效的工具、资源、插件等,****助力开发者成长!

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

    点个在看 你最好看

    原文始发于微信公众号(GitHub今日热榜):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> Java 线上问题定位处理利器——Arthas , 附使用案例


     上一篇
    面试官——消息队列这些我必问! 面试官——消息队列这些我必问!
    作者:mousycoder segmentfault.com/a/1190000021054802 segmentfault.com/a/1190000021054802 消息队列连环炮 项目里怎么样
    2021-04-05
    下一篇 
    Doxygen 文档生成工具使用教程 Doxygen 文档生成工具使用教程
    一、什么是Doxygen?Doxygen 是一个程序的文件产生工具,可将程序中的特定批注转换成为说明文件。通常我们在写程序时,或多或少都会写上批注,但是对于其它人而言,要直接探索程序里的批注,与打捞铁达尼号同样的辛苦。大部分有用的批注都是属
    2021-04-05