【死磕JVM】用Arthas排查JVM内存 真爽!我从小用到大

image.png

Arthas是啥

当我们系统遇到JVM或者内存溢出等问题的时候,如何对我们的程序进行有效的监控和排查,就发现了几个比较常用的工具,比如JDK自带的 jconsole、jvisualvm还有一个最好用的工具——jprofiler,但是这个是收费的,或者除了很有钱的公司,一般很少人会用这个,还有一个就是我们今天的主角——Arthas ,为什么今天会重点讲这个呢?

官网地址:http://arthas.gitee.io/
GitHub地址:https://github.com/alibaba/arthas/

Arthas 是Alibaba开源的Java诊断工具,采用命令行交互模式,提供了较为丰富的功能,主要还是他是免费里面的算是好用且功能比较强大的一个JVM排查的插件,在了解这个利器之后,发现还是挺好用的,而且支持的功能也比较全面,那么Arthas到底可以为我们做哪些事情呢?

  1. 提供性能看板,包括线程、cpu、内存等信息,并且会定时的刷新。
  2. 根据各种条件查看线程快照。找出cpu占用率最高的n个线程
  3. 输出jvm的各种信息,如gc算法、jdk版本、ClassPath等
  4. 遇到问题无法在线上 debug,热部署加日志直接替换
  5. 查看某个类的静态属性,也可以通过ognl语法执行一些语句
  6. 查看已加载的类的详细信息,这个类从哪个jar包加载的,查看类的方法的信息
  7. dump 类的字节码到指定目录
  8. 直接反编译指定的类
  9. 快速定位应用的热点,生成火焰图
  10. 可以监控到JVM的实时运行状态

以前,你碰到这些问题,解决的办法大多是,修改代码,重新上线。但是在大公司里,上线的流程是非常繁琐的,如果为了多加一行日志而重新发布版本,无疑是非常折腾人的。但是阿里巴巴开源的Arthas
有了更为优雅的线上调试方法。

Arthas 支持JDK6,同时可以在 Linux/Mac/Windows上运行,自动Tab 补全功能,更方便我们定位问题和诊断

下载地址:https://arthas.gitee.io/download.html
你可以下载zip的包我下载的是arthas-packaging-3.5.0-bin.zip
或者通过命令去下载

wget https://alibaba.github.io/arthas/arthas-boot.jar

使用手册

1. 快速启动

当我们下载好之后,我们直接通过命令启动就可以java -jar arthas-boot.jar,但是在此之前我们需要通过检测的代码来挂靠到Arthas上面

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class FullGCTest {


    //模拟银行卡的类
    private static class CardInfo {
        //小农的银行卡信息记录
        BigDecimal price = new BigDecimal(10000000.0);
        String name = "牧小农";
        int age = 18;
        Date birthdate = new Date();

        public void m() {}
    }

    //线程池 定时线程池
    //50个,然后设置 拒绝策略
    private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
            new ThreadPoolExecutor.DiscardOldestPolicy());

    public static void main(String[] args) throws Exception {
        executor.setMaximumPoolSize(50);

        for (;;){
            modelFit();
            Thread.sleep(100);
        }
    }

    /**
     * 对银行卡进行风险评估
     */
    private static void modelFit(){
        List<CardInfo> taskList = getAllCardInfo();
        //拿出每一个信息出来
        taskList.forEach(info -> {
            // do something
            executor.scheduleWithFixedDelay(() -> {
                //调用M方法
                info.m();

            }, 2, 3, TimeUnit.SECONDS);
        });
    }

    private static List<CardInfo> getAllCardInfo(){
        List<CardInfo> taskList = new ArrayList<>();
        //每次查询100张卡出来
        for (int i = 0; i < 100; i++) {
            CardInfo ci = new CardInfo();
            taskList.add(ci);
        }

        return taskList;
    }
}

这个是上篇文章讲述的案例,感兴趣的可以了解一下。

首先我们需要使用javac 命令将Java文件进行编译javac FullGCTest.java进行编译,然后打印GC日志,进行风险监控打印GC日志:
java -Xms200M -Xmx200M -XX:+PrintGC FullGCTest

Arthas启动命令:java -jar arthas-boot.jar

在这里插入图片描述

我们就看到了我们刚才启动的FullGCTest的应用程序,我们输入编号 1 回车,这样我们就把Arthas挂靠到我们的程序上,接下来我们只需要做对应的命令操作就可以了


在这里插入图片描述

命令详情文档:https://arthas.aliyun.com/doc/commands.html

2. 功能列表

命令 详细说明
jvm 查看当前JVM信息
thread 查看当前JVM的线程堆栈信息
watch 方法执行数据观测
dashboard 当前系统的实时数据面板
trace 方法内部调用路径,并输出方法路径上的每个节点上耗时
stack 输出当前方法被调用的调用路径
tt 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
vmoption 查看,更新JVM已加载的类信息
sc 查看JVM已加载的类信息
sm 查看已加载类的方法信息
jad 反编译指定已加载类的源码
classloader 查看classloader的继承树,urls,类加载信息
heapdump 类似jmap命令的heap dump 功能

jvm

在这里插入图片描述

OPERATING-SYSTEM:系统相关参数

THREAD相关:

  • COUNT : JVM当前活跃的线程数
  • DAEMON-COUNT : JVM当前活跃的守护线程数
  • PEAK-COUNT: 从JVM启动开始曾经活着的最大线程数
  • STARTED-COUNT: 从JVM启动开始总共启动过的线程次数
  • DEADLOCK-COUNT: JVM当前死锁的线程数

MEMORY

FILE-DESCRIPTOR(文件描述符相关):

  • MAX-FILE-DESCRIPTOR-COUNT:JVM进程最大可以打开的文件描述符数
  • OPEN-FILE-DESCRIPTOR-COUNT:JVM当前打开的文件描述符数

thread 命令

参数说明:

命令 详细说明
id 线程id
[n:] 指定最忙的前N个线程并打印堆栈
[b] 找出当前阻塞其他线程的线程
[i] 指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200
[--all] 显示所有匹配的线程

打印当前最忙的N个线程并打印堆栈
thread -n 3

在这里插入图片描述

thread 查看所有线程


在这里插入图片描述

thread 17: 显示指定线程的运行堆栈


在这里插入图片描述

thread -i: 指定采样时间间隔

thread -i 1000 : 统计最近1000ms内的线程CPU时间。
thread -n 3 -i 1000 : 列出1000ms内最忙的3个线程栈

dashboard 命令

运行程序时,会显示当前程序的实时信息,如qps, rt, 错误数, 线程池信息等等

在这里插入图片描述

数据说明:

  • ID: Java级别的线程ID
  • NAME: 线程名
  • GROUP: 线程组名
  • PRIORITY: 线程优先级, 1~10之间的数字,越大表示优先级越高
  • STATE: 线程的状态CPU%: 线程的cpu使用率。比如采样间隔1000ms,某个线程的增量cpu时间为100ms,则cpu使用率=100/1000=10%
  • DELTA_TIME: 上次采样之后线程运行增量CPU时间,数据格式为秒
  • TIME: 线程运行总CPU时间,数据格式为分:秒
  • INTERRUPTED: 线程当前的中断位状态
  • DAEMON: 是否是daemon线程
在这里插入图片描述

参数说明:

参数名称 详细说明
id 刷新实时数据的时间间隔 (ms),默认5000ms
[n:] 刷新实时数据的次数

sc 命令

查看JVM已加载的类信息,通过SC我们可以看到我们这个类的详细信息,包括是从哪个jar包读取的,他是不是接口/枚举类等,甚至包括他是从哪个类加载器加载的。

参数说明:

参数名称 详细说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
[d] 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。如果一个类被多个ClassLoader所加载,则会出现多次
[E] 开启正则表达式匹配,默认为通配符匹配

sc -d *CardInfo: 打印类的详细信息

在这里插入图片描述

sc -d -f *CardInfo:打印类的Fiedld信息

在这里插入图片描述

heapdump + jhat分析

heapdump:类似于jmap命令

创建到指定文件夹下:

[arthas@365564]$ heapdump /usr/local/mxn/dump.hprof
Dumping heap to /usr/local/mxn/dump.hprof ...
Heap dump file created

创建成功后,我们就可以在指定文件夹下看到对应的dump文件,然后使用命令jhat dump.hprof,生成文件,成功后我们就可以通过IP+端口进行访问了

在这里插入图片描述

访问:


在这里插入图片描述

然后我们就可以通过IP+端口去访问它了,里面有个他的other,我们拉到最底下,找
Show instance counts for all classes (including platform)

在这里插入图片描述

从下面我们可以分析出来哪个类包含的对象最多,分析出来哪个类产生的对象

在这里插入图片描述

这个里面最强大的功能还是叫做 Execute Object Query Language (OQL) query,这个里面可以显示有哪些对象,对象有多少个字节和引用,可以观察到哪个对象产生了问题,如下图所示,显示所有String对应的对象

在这里插入图片描述

在这里插入图片描述

搜索点进去之后我们还能看到这个对象到底占用了多少个字节,有多少个引用指向了这个Object,这个OQL的语法也是很灵活,我们可以使用where条件去过滤

在这里插入图片描述

jad

jad:反编译某个类,或者反编译某个类的某个方法,动态代理生成类的问题定位 第三方的类(观察代码) 版本问题(确定自己最新提交的版本是不是被使用)

在这里插入图片描述

有人可能会问这个有啥用,源码我不是自己就知道吗?因为有时我们经常会不确定线上或者测试环境的包是否是我们修改过的,这时候就可以通过jad反编译来看下,是否是最新的代码

redafine

redafine:热替换,动态更新代码,不用重启jvm目前有些限制条件:只能改方法实现(方法已经运行完成),不能改方法名, 不能改属性 m() -> mm()

比如我们在线上环境有个class确认有问题,想要重新替换,一般情况下只能停掉服务器重新发布,在普通的小公司这样是可以的,但是在大规模公司京东淘宝这样的是不能停的,因为整个流程是非常复杂的,那怎么办呢?大家可以看到下面的案例

首先我们新建一个测试案例:

public class T{
    public static void main(String[] args) throws Exception{
                    for(;;){
                    System.in.read();
                    new TT().m();
                }
        }
}
public class TT{
        public void m(){
        System.out.println(2);
    }
}

使用命令javac *.java,编译成class文件,然后运行 T 文件

[root@VM-0-7-centos t]# java T
a
2
2

当我们输入a的时候打印2,但是我们上线以后才发现,我们需要输出的1,这个是如果要从本地更改要重新发布上线,为了这一个修改,明显是不值当的,但是如果我们用 redafine 热部署就可以帮助我们直接替换,不用重新发布jvm

然后我们将 T 这个程序挂靠到 Arthas 上面去


在这里插入图片描述

然后我们直接修改 TT.java 程序 vi TT.java,将里面打印2的值修改成1

public class TT{
        public void m(){
            System.out.println(1);
        }
}

然后编译执行 ```javac TT.java ````

在回到我们挂靠的Arthas 上面执行 redefine /usr/local/mxn/fuccGc/t/TT.class

[arthas@398842]$ redefine /usr/local/mxn/fuccGc/t/TT.class
redefine success, size: 1, classes:
TT

执行成功
大家可以看到我们在没有重新启动的情况下成功替换了class文件


在这里插入图片描述

watch

watch:方法执行的数据观测,可以通过watch指令,来监控某个类,监控后,运行下你的功能,复现下场景,arthas会提供给你具体的出参和入参,帮助你排查故障

trace

输出方法调用路径,并输出耗时,这个指令对于优化代码非常的有用,可以看出具体每个方法执行的时间,如果是for循环等重复语句,还能看出n次循环中的最大耗时,最小耗时,和平均耗时,完美!

tt

在我们对某个方法开启tt后,会记录每一次调用(我们可以设置最大监控次数)的入参和返回参数,并能对这些不同时间下调进行观测

[arthas@405136]$ tt -t FullGCTest modelFit

在这里插入图片描述

命令参数解析-t
tt 命令有很多个主参数,-t 就是其中之一。这个参数的表明希望记录下类 *Test 的 print 方法的每次执行情况。
-n 3
当你执行一个调用量不高的方法时可能你还能有足够的时间用 CTRL+C 中断 tt 命令记录的过程,但如果遇到调用量非常大的方法,瞬间就能将你的 JVM 内存撑爆。

此时你可以通过 -n 参数指定你需要记录的次数,当达到记录次数时 Arthas 会主动中断tt命令的记录过程,避免人工操作无法停止的情况。

ognl表达式

ognl表达式

OGNL特殊用法请参考:https://github.com/alibaba/arthas/issues/71
OGNL表达式官方指南:https://commons.apache.org/proper/commons-ognl/language-guide.html

调用静态函数:ognl '@java.lang.System@out.println("hello")'
获取静态类的静态字段:ognl '@FullGCTest@random'

Arthas还支持Web Console,详见:https://alibaba.github.io/arthas/web-console.html

总结

Arthas是一个线上Debug神器,相比于其他工具,Arthas有着比较全面的功能,上手也比较容易,对于刚开始入门的小伙伴也是可以轻松掌握的,对于文中有不懂或者有问题的小伙伴,大家可以在下面留言评论。

原创不易,希望大家多多捧场,记得一键三连!!!

我是牧小农,怕什么真理无穷,进一步有进一步的欢喜,大家加油!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容