JVM学习笔记之GUI监控工具

目录

背景

jConsole

本地连接:连接本地计算机一个正在运行的JVM进程

远程连接:连接JMX代理

高级连接:使用特殊的URL连接JMX代理

VisualVM

插件安装

连接方式

本地连接:连接本地计算机上的一个JVM进程

远程连接:远程远程服务器上的某个JVM进程

基本使用

堆快照文件的生成与读取

生成dump文件

查看快照文件

线程快照文件的生成与读取

生成

查看线程dump文件

抽样

CPU

内存

EclipseMAT

获取和分析dump文件

获取dump文件

分析dump文件

内存泄漏

内存泄漏和内存溢出关系

内存泄漏的8种情况

使用OQL查询对象信息

JProfiler

两种数据采集方式

遥感监测

内存视图

堆遍历

CPU视图

线程视图

Arthas

启动与退出

基础指令的使用

dashboard与thread指令

dashboard:当前系统的实时数据面板

thread:当前JVM的线程堆栈信息

sysprop命令和heapdump命令

sysprop:查看系统属性

heapdump:导出转储文件(--live可以指定只获取存活的对象)

sc与sm命令

sc命令:找到某个类

sm命令:查看类的方法

jad、mc和classloader命令

jad命令:反编译某个类

mc命令:编译java文件成class文件

classloader命令:查看类加载器相关信息

monitor、watch、trace、stack和tt命令

monitor命令:方法执行监控

watch命令:方法执行的数据监测

trace命令:方法内部调用路径

stack命令:方法被调用的路径

tt命令:方法执行数据的时空隧道

其他相关命令

JavaMissionControl

Btrace

FlameGraphs火焰图

结语

背景

本文介绍一些常见的图形化JVM监控工具,掌握其中一两个就行,多图预警

jConsole

jConsole是jdk5开始就自带的java监控和管理控制台,是一个基于JMX(Java Management Extensions)的GUI性能监控工具,在命令行输入jconsole.exe即可启动

本地连接:连接本地计算机一个正在运行的JVM进程

本地连接需要执行程序和运行JConsole的是同一个用户,JConsole使用文件系统的授权通过RMI连接器连接到平台的MBean服务器上,而这种从本地连接的监控能力只有Sun的JDK有

启动一个样例进程后,上面窗口的本地进程里可能没有我们的JVM进程,这时关闭新建连接对话框,在连接->新建连接中再打开一个即可找到我们的JVM进程:

双击之即可,然后在弹出的对话框中选择不安全的连接

就可以看到JVM进程监视的动态变化图了:

在线程标签中,可以检测死锁,就拿以下代码来测试:

public class ThreadDeadLock {
    public static void main(String[] args) {
        StringBuilder builder1 = new StringBuilder();
        StringBuilder builder2 = new StringBuilder();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (builder1) {
                    builder1.append(1);
                    builder2.append("a");

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


                    synchronized (builder2) {
                        builder1.append(2);
                        builder2.append("b");
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (builder2) {
                    builder1.append(1);
                    builder2.append("a");


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


                    synchronized (builder1) {
                        builder1.append(2);
                        builder2.append("b");
                    }
                }
            }
        }).start();
    }
}

运行并在JConsole中新建连接后,线程标签下的内容如下所示

点击检测死锁,就会检测出发生死锁的线程

点击其中一个,就会看到等待的锁对象及其持有者,以及自己拥有的锁对象

远程连接:连接JMX代理

使用service:jmx:rmi:///jndi/rmi://hostname:port/jmxrmi通过RMI连接器连接到一个JMX代理,JConsole为了建立连接,需要在环境变量中设置mx.remote.credentials来指定用户名和密码以进行授权

高级连接:使用特殊的URL连接JMX代理

一般情况是使用自己定制的连接器而非RMI提供的连接器来连接JMX代理,或者是一个使用JDK4的实现了JMX和JMX Remote的应用

VisualVM

这也是一个jdk自带的监视工具,从JDK6 Update7后就成了jdk发行版的一部分了,直接在命令行输入jvisualvm即可启动

插件安装

可以从http://visualvm.github.io/pluginscenters.html上下载插件,或者在VisualVM窗口中的工具->插件中安装可用的插件

还可以在idea中安装VisualVM Launcher插件(需要用管理员权限打开idea):

在idea中装好后,进入Settings->VisualVM Launcher中配置jvisualvm可执行文件的路径和JDK_HOME即可

而后在主界面上方,就会看到用visualVM来运行、调试或者启动visualVM的按钮了

点击用visualVM启动的按钮,结果如下:

连接方式

本地连接:连接本地计算机上的一个JVM进程

双击本地选项中的一个JVM进程即可:

远程连接:远程远程服务器上的某个JVM进程

步骤如下:

1)、确定远程服务器的IP地址

2)、添加JMX以具体监控远程服务器上的某个JVM进程

3)、修改bin/catalina.sh文件,连接远程的tomcat

4)、在../conf中添加jmxremote.access和jmxremote.password文件

5)、将服务器地址改为公网IP

6)、设置阿里云安全策略和防火墙策略

7)、启动tomcat,查看tomcat启动日志和端口监听

8)、在JMX中输入端口号、用户名和密码登录

基本使用

示例代码:

// -Xmx600m -Xms600m -XX:SurvivorRatio=8
public class OOMTest {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<Picture> pictures = new ArrayList<>();
        while (true) {
            byte[] data = new byte[10 * 1024];
            pictures.add(new Picture(data));
            Thread.sleep(50 * 1000);
        }
    }
}

class Picture {
    private byte[] data;

    public Picture(byte[] data) {
        this.data = data;
    }
}

打开visualVM并连接到OOMTest进程后得到的界面如下所示,概述页可以看到java版本、pid、主类、JVM参数和系统属性等信息:

在监视页面中,可以看到CPU、内存、类、线程的指标随时间的实时变化情况

在线程页面中,可以看到各个线程及其状态,如下所示。这里有10个实时线程,九个是守护线程,剩下的就是main线程。main线程被标记为休眠,因为main线程的绝大部分执行时间都花在了Thread.sleep()上

抽样器负责进行数据的采集,Profiler负责进行堆内存的数据分析

VisualGC可以进行GC的可视化,并且查看堆中各个区域的空间变化情况:

堆快照文件的生成与读取

生成dump文件

点击监视界面的堆dump按钮生成快照:

我们可以在左侧进程下面看到生成的快照:

若要存盘,右击它,点击另存为,选择路径设置名称即可

保存后,删除快照,磁盘上的快照文件依旧存在

查看快照文件

点击visualVM主界面的文件->装入按钮,选择dump文件,点击确定即可打开:

打开后的界面如下所示,在概述页里可以看到类总数、对象总数、GC根结点数等信息:

在类页面中,可以看到各个类的全类名,按照实例数降序排列:

实例数页面需要我们双击选择一个类才能进行有效显示,就拿String类为例,可以看到每个对象的各个属性等信息:

在OQL页面中,可以进行一些查询,比如右下方给出的一些例子,双击其中一个,就会在左侧的查询编辑器中得到查询语句:

点击执行就会在上方查询结果中看到的结果:

点击其中一个对象,就可以跳转到实例数页面中查看其详细信息,比如点击#936,结果如下

在类页面中,我们还可以与另一个堆文件进行比较,点击右上方的与另一个堆转储进行比较:

选择通过文件->选择我们的文件,点击确定:

这样就会看到和我们新选择的dump文件(0019)相比,当前dump文件(2809)的类对象数量和对象的占用字节大小变化情况:

线程快照文件的生成与读取

生成

用我们的死锁代码为例,在visualVM中双击连接此进程,进入线程页面,可以看到已经检测到了死锁:

点击右面的线程Dump,会在右侧进程下面生成一个线程转储:

同样右击之,另存为保存到磁盘上就可以了,只不过这里生成的文件为线程dump(tdump)文件

查看线程dump文件

同样,文件->装入,文件类型选择tdump,选择我们要打开的转储文件,打开即可

在打开的界面中,可以找到Thread-1和Thread-0的信息,明显可以看到双方占有了对方等待的线程锁,形成了环路等待,造成死锁:

抽样

在visualVM主界面的抽样页面,我们可以对CPU和内存进行抽样分析

CPU

点击CPU按钮就可以进行CPU抽样,用来分析热点方法,也可以生成快照:

也可以点击线程 CPU时间来查看每个线程占用的CPU时间:

内存

点击内存按钮就可以对堆内存进行采样分析,并生成快照:

也可以查看每个线程的分配情况:

EclipseMAT

这是基于Eclipse开发的堆内存分析工具,用于查找内存泄漏和内存消耗情况,可以单独使用,下载地址:http://www.eclipse.org/mat/downloads.php

下载后解压,运行mat/MemoryAnalyzer.exe文件即可:

可以看到欢迎界面如下:

获取和分析dump文件

dump文件主要包括所有的对象信息(对象实例、成员变量、存储于栈中的基本类型值和存储于堆中的其他对象引用值)、所有类信息(类加载器、类名、父类、静态变量等)、GC根结点到所有对象的引用路径、线程信息(线程调用栈及TLS局部变量)

mat只能处理主流的二进制堆转储文件,比如Sun、HP、SAP所采用的HPROF文件和IBM的PHD堆存储文件;不过mat最引人注意的还是能够快速生成内存泄漏报表,方便我们定位问题和分析问题,但很多问题还是需要我们从mat展现的信息中通过经验和直觉去判断才能发现。

示例代码如下:

// -Xmx600m -Xms600m -XX:SurvivorRatio=8
public class OOMTest {    
    public static void main(String[] args) throws InterruptedException {
        ArrayList<Picture> pictures = new ArrayList<>();
        while (true) {
            byte[] data = new byte[10 * 1024];
            pictures.add(new Picture(data));
            Thread.sleep(50 * 1000);
        }    }
}

class Picture {
    private byte[] data;
    public Picture(byte[] data) { 
       this.data = data;
    }
}

获取dump文件

目前生成堆转储文件的方式有四种:

1)、通过jmap生成;

2)、通过配置-XX:+HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath=path/to/file.dump参数来在OOM时生成;

3)、visualVM导出;

4)、mat导出

使用mat导出时,先以管理员身份打开mat,然后点击File->Acquire Heap Dump,选择我们的OOM进程和导出的hprof文件路径

点击Finish后,在弹出的对话框中再点击Finish。其中三个选项分别表示:泄漏疑点检测(自动检测堆转储文件来检查泄漏疑点,报告哪些对象是存活的并且它们为何没有被垃圾回收)、组件报告(分析一系列引发可疑内存问题的对象,比如重复字符串、空的集合、finalizer、弱引用等)和重新打开之前的运行报告(已有的报告以zip文件的方式存在,和dump文件存在于同一个目录内)

选择内存泄漏检测就可以了,出现的界面如下所示:

并且在hprof文件所在目录,会多出很多的文件,其中zip文件就是泄漏疑点检测生成的报告文件:

分析dump文件

运行OOM示例代码后使用jhat生成dump文件:

PS C:\WINDOWS\system32> jps -l
19360 org.jetbrains.jps.cmdline.Launcher
13240 OOMTest
3672
3928
1980 sun.tools.jps.Jps

PS C:\WINDOWS\system32> jmap -dump:format=b,file=C:\Users\songzeceng\Desktop\oom.hprof 13240
Dumping heap to C:\Users\songzeceng\Desktop\oom.hprof ...
Heap dump file created

PS C:\WINDOWS\system32>

打开mat,点击File->Open Heap Dump打开堆栈文件,选中我们刚生成的oom.hprof

之后的处理和上面用mat导出hprof的就一样了,我们点击概览,进入概览标签页:

呈现出来的有使用的堆大小、捕获的类数量、对象数量、类加载器数量,下面的饼图表示存活的大对象,我们把鼠标悬停在一个对象上面,就会在左侧看到这个对象的详情:

在概述标签上面一行,我们点击右数第三个,选择堆转储概览

就可以看到此堆转储文件的更详细的概览信息,包括使用的堆大小、对象数量、类数量、类加载器数量、GC根结点数量、转储文件格式、转储文件生成时间、生成日期、系统位数、转储文件路径、大小、系统属性、线程概览等

而主概览标签页下还有直方图、支配树、大对象、重复类、泄漏疑点等选项

我们把代码稍加修改

// -Xmx600m -Xms600m -XX:SurvivorRatio=8
public class OOMTest {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<Picture> pictures = new ArrayList<>();
        while (true) {
            byte[] data = new byte[new Random().nextInt(10 * 500) * 1024]; // 让byte数组大小随机
            pictures.add(new Picture(data));
            Thread.sleep(50 * 1000);
        }
    }
}

class Picture {
    private byte[] data;

    public Picture(byte[] data) {
        this.data = data;
    }
}

再次执行上面的过程进入分析界面,打开直方图:

得到的表格如下图所示,四列分别表示类名、类对象数量、类占用浅堆大小和类占用深堆大小

浅堆大小表示如果一个类对象中有引用字段,只把这个引用字段的大小计算为4字节,再加上对象头八字节,必要时还要对齐到八字节的整数倍,而不进一步累加引用字段中的字段,而深堆则要进一步累加。比如Student对象中有Score对象,Score对象中有一个int和两个float,计算浅堆大小时,直接把此Score对象当成四字节(也就是一个引用的大小),再加上八字节的对象头,是为十二字节,最后八字节对齐,得到浅堆大小为十六字节;

计算深堆大小时,首先要明确保留集的概念:对象A的保留集是指仅被对象A所持有(直接或间接持有)的对象集合,这些对象包括A自己都可以在A被GC时回收,而深堆大小就是对象保留集中所有对象的浅堆大小之和,此数值也是对象被回收之后可以释放的真实空间大小。

还有一个概念叫做对象的实际大小,这表示一个对象所能触及的所有对象的浅堆之和,这个数显然不小于此对象的深堆大小。以下图为例:

A的浅堆大小为A,深堆大小为A+D,保留集为{A, D},实际大小为A + C + D;B一样的道理

回到mat,对于直方图,我们可以右击->Columns->Sort By来进行排序

比如选择对象数量,结果如下所示

也可以在上方的选项中选择按什么分组

比如选择按包分组,结果如下所示,选择一个包可以查看这个包下的类对应的信息

还可以在表格第一行输入正则表达式来查询我们想要的类信息:

比如输入Picture,得到的结果如下图所示

还可以进行不同转储文件的直方图比较:

点击后选择一个转储文件即可

得到就是以当前转储文件为标准,新选择的转储文件直方图的变化情况:

回到概览主界面,我们还可以查看每个线程的内存使用情况:

打开后,找到我们的主线程,线程栈里面有sleep()方法和main()方法,以及局部变量字节数组,我们查看以下主方法,可以看到String[]对象(形参)和ArrayList对象

其中ArrayList对象里的elementData数组就保存着我们的数据——14个Picture对象,而数组大小为15是ArrayList的扩容机制导致的

回到概览主界面,选择泄漏疑点分析

得到的结果如下所示,包括可疑的线程信息、局部变量大小及占比(这里97%的堆空间占用都是来自于此),以及对象类型

我们可以查看线程的调用栈、涉及可疑对象的调用栈和细节:

查看线程调用栈的结果如下图所示,可以看到main()和sleep()的调用关系

查看涉及可疑对象的调用栈的结果如下,可以看到可疑对象ArrayList的调用栈被蓝色标注了(主线程->main()方法->ArrayList局部变量)

细节界面如下所示,包括描述信息、到达此对象的最短引用路径、此对象在支配树上的对象:

我们左键点击支配树上的ArrayList对象,选择List objects->with outgoing references或者with ingoing references,来查看它引用的或引用它的对象列表

这里它引用的(Outgoing references)对象列表如下所示,可见就是ArrayList里面的东西:

引用它的(Ingoing references))对象列表如下所示,可见就是我们的主线程:

最后我们看一下支配树,此概念来自于图论,体现了对象实例间的支配关系:在对象引用图中,如果所有指向对象B的路径都经过对象A,那么则认为对象A支配对象B;如果对象A是离对象B最近的一个支配对象,则认为对象A是对象B的直接支配者。支配树是基于对象间的引用图所建立的,基本性质如下:

  • 对象A的子树(所有被对象A支配的对象集合)是A的保留集,亦即深堆;
  • 如果对象A支配对象B,则A的直接支配者也支配B;
  • 支配树的边和对象引用图的边不一一对应。

如下图所示,左图表示对象引用图,右图表示左图对应的支配树。对象A和B都由根对象直接支配,由于在到达对象C的路径中,可以经过A,也可以经过B,所以C的直接支配者也是根对象。对象F和D相互引用,且到F的所有路径都经过D,那么D是F的直接支配者。同样,到对象D的所有路径也都经过对象C,那么D的直接支配者是C

同理,对象E支配G,到达H的路径可以通过D,也可以通过E,因此D和E都不是H的支配者,而经过C的既可以到达D也可以到达E,所以C是H的直接支配者

回到mat的概览主界面,点击Dominator Tree,就能进入支配树界面

支配树界面如下:

可以看到主线程对象、ArrayList和Picture对象的支配关系:

内存泄漏

内存泄漏指的是:只有对象不会再被程序使用了,但GC又不能回收它们的情况。但实际上,如果对象的生命周期太长甚至发生了OOM,我们也可以称之为发生了内存泄漏

内存泄漏和内存溢出关系

内存泄漏指的是申请了内存不被释放,比如申请了1024M的内存,一次GC却只回收了512M内存,而剩下的512M不能被回收也不起作用,就发生了内存泄漏;内存溢出就是申请内存时没有足够的内存以使用。

显然,内存泄漏的增多会导致内存溢出

内存泄漏的8种情况

1)、静态集合类:如果HashMap、LinkedList是静态的,那么其生命周期与JVM程序一致,导致容器中的对象在程序之前不能被释放,从而发生内存泄漏。

public class MemoryLeak {
    static List list = new LinkedList();
    
    public void f() {
        list.add(new Object());
    }
}

2)、单例模式:和静态集合类导致内存泄漏的原因类似,因为单例的静态特性,其生命周期和JVM的等长,如果单例对象持有外部对象的引用,那么外部对象就不会被回收

3)、内部类持有外部类:如果一个外部类的实例方法返回了一个内部类的实例对象,并且这个内部类对象被长期引用,那么就发生外部对象的内存泄漏,因为内部类对象会引用外部类的实例对象

4)、各种连接:各种数据库连接、网络连接、IO连接等,如果不在finally{}块中手动关闭,那就会导致大量对象无法被回收,从而导致内存泄漏

5)、变量不合理的作用域:如果变量的定义作用范围大于其使用范围,且没有及时将其设为null,就可能发生内存泄漏

public class UsingRandom {    
    private String msg;

    public void receiveMsg() {
        readFromNet(); // 从网络接收数据保存到msg中
        saveDB(); // 把msg保存到数据库中
    }
}

在上面的代码中,saveDB()之后msg对象不在被使用,但此对象生命周期和UsingRandom对象等长,所以导致不能被及时回收,可能会发生内存泄漏。我们可以把msg对象设置为方法中的临时变量,或者在saveDB()后把msg设置为null

6)、改变哈希值:当一个对象被保存到HashSet等集合中后,就不能修改此对象中参与计算哈希值的字段了;否则就会导致修改对象后的哈希值与存储时的不一致,这样会导致哈希集合的contains()方法返回不出存储的对象,也会导致不能从哈希集合中单独删除存储的对象,造成了内存泄漏。

这也是为何String被设置成了不可变类型,这样就可以放心地把String存入哈希集合或者把String当成哈希映射的键了。因此,要把自定义的类存入哈希集合时,要保证对象的哈希值不变。

7)、缓存泄漏:一旦把对象引用缓存下来,就很容易遗忘,我们可以使用WeakHashMap来实现缓存,其特点是当除了自身有对键的引用外,此键没有别的引用,映射就会丢弃这一键值对

8)、监听器和回调:如果客户端在注册回调之后却没有显式注销,就会发生积累,导致内存泄漏。我们可以只保存回调的弱引用,比如WeakHashMap中的键

使用OQL查询对象信息

查询ArrayList的所有对象:

select * from java.util.ArrayList

执行结果(点击编辑框上面的红色感叹号执行):

查询字段:

SELECT v.elementData FROM java.util.ArrayList v

执行结果

查询保留集:

SELECT as retained set * FROM java.util.ArrayList

执行结果:

查询长度大于20的字节数组:

select * from char[] s where s.@length > 20

执行结果:

查找不为空的字符串:

select * from java.lang.String s where s.value != null

执行结果:

查找ArrayList对象长度:

SELECT v.elementData.@length FROM java.util.ArrayList v

查询结果:

JProfiler

这是一款功能强大但收费的idea性能诊断工具,下载地址:https://www.ej-technologies.com/products/jprofiler/overview.html,安装完成后的主界面如下图所示:

我们可以配置一下,让JProfiler和idea集成,在JProfiler主界面点击Session->IDE Integrations

选择IDE版本(前提要关掉对应的IDE,虽然我的idea早已升级到了2020.3版本,但2019的配置目录还在,所以选择idea2019也是可以的),点击Integrate

在弹出的对话框中点击Proceed,然后选择idea的配置根目录(.Idea打头的)

点击Choose,就可以了

然后我们打开idea,安装插件JProfiler

安装完后在Tools->JProfiler中设置JProfiler可执行文件的路径:

这样,在启动工具栏里就有了JProfiler的图标:

我们点击左边的那个,就可以用JProfiler调试程序,在弹出的界面里选择Sampling采样,点击OK即可

就可以看到指标随时间变化的实时情况了

两种数据采集方式

打开JProfiler主界面上的Start Center,会弹出一个对话框让我们选择启动模式:

如果我们的java程序已经启动,就选择第二个——Quick Attach即可

双击我们要连接的进程,JProfiler就会与之建立连接

对于这两种数据采集方式,说明如下:

Instrumentation重构模式是JProfiler的全功能模式,在class加载之前,JProfiler把相关功能代码写入到需要分析的类的字节码之中,对正在运行的JVM有一定影响。其优点是功能强大,调用堆栈的信息可以被准确获取;缺点是当分析的类较多时,会产生比较高的CPU开销,影响应用性能,因此一般与Filter配合使用,只对特定的类或包进行分析;

Sampling采样模式则是每隔一定时间(5ms)将每个线程栈中方法栈信息统计出来,优点是CPU开销非常低,对应用性能影响小;缺点是一些数据不能被提供,比如方法的调用次数、执行时间等

遥感监测

主要关注左侧的导航栏,包括遥感监测、内存、堆、CPU、线程、锁等:

其实上面的视图就是遥感监测视图,在左侧二级导航栏中可以选择要查看的内容,比如内存:

亦可以在内存池中选择要查看的内存区域:

内存视图

选择左侧的Live Memory,就进入了内存视图,包括类、类实例个数、所占堆空间大小三列:

我们选择上面的Mark Current,就可以查看堆中对象情况的实时变化:

也可以选择聚集层次,比如按包展示:

我们需要关注的情况有:

  • 频繁创建的java对象,比如死循环或者循环次数过多,这时Instance Count一列值就会过大;
  • 大对象,比如读取文件时,byte[]应该边读边写,这时Size一列值就会过大,并且Instance Count却不多;
  • 内存泄漏,这时可以进入Recorded Objects进行查看(这里没啥问题):

堆遍历

在内存视图中右击某个类,点击Show Selection In Heap Walker,就进入了堆遍历界面

在弹出的对话框中点击OK后,就进入了下面的界面:

然后右击这个类,选择Use Select Objects

在References一栏中选择入引用,就是查看谁引用了它的对象

OK后,在出来的界面中选择一个元素,展开到极限,会发现是main()方法的ArrayList对象引用了这个Picture对象

也可以右击某个对象,选择Show In Graph

会得到此对象的引用图,当前对象会被蓝色标注:

点击里面的+号可以显示更详细的引用关系:

当要保存为堆转储文件时,点击右上角的Save Snapshot,然后选择路径、设置文件名即可

打开堆转储文件时,点击Session->Open Snapshot

然后选择想要打开的文件即可:

CPU视图

点击左侧导航栏里的CPU Views,就进入了CPU视图

点击记录就开始记录数据(以JDBC Demo为例),为了显示数据,我们先在Method Statistics中点击Record Statistics

再点击Stop Statistics

然后调用树中就会看到函数的执行耗时与占比:

线程视图

点击左侧的Threads就进入的线程视图:

也可以生成转储文件:

Arthas

这是阿里开源的java诊断工具,可以在线排查问题,无需重启,动态跟踪java代码,实时监控JVM状态,地址:https://arthas.aliyun.com/zh-cn/,官方教程:https://arthas.aliyun.com/doc/quick-start.html

启动与退出

PS C:\Users\songzeceng\Desktop\软件\02_软件> java -jar .\arthas-boot.jar
[INFO] arthas-boot version: 3.4.5
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 15488
  [2]: 4900 org.jetbrains.jps.cmdline.Launcher
  [3]: 11100 test.ThreadDeadLock

输入序号,就可以连接了

PS D:\develop> java -jar .\arthas-boot.jar
[INFO] arthas-boot version: 3.4.5
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 15488
  [2]: 4900 org.jetbrains.jps.cmdline.Launcher
  [3]: 11100 test.ThreadDeadLock
3
[INFO] Start download arthas from remote server: https://arthas.aliyun.com/download/3.4.6?mirror=aliyun
[INFO] File size: 11.99 MB, downloaded size: 5.38 MB, downloading ...
[INFO] File size: 11.99 MB, downloaded size: 10.94 MB, downloading ...
[INFO] Download arthas success.
[INFO] arthas home: C:\Users\songzeceng\.arthas\lib\3.4.6\arthas
[INFO] Try to attach process 11100
[INFO] Attach process 11100 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.
/  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'




wiki      https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version   3.4.6
pid       11100
time      2021-02-04 22:23:27

[arthas@11100]$

输入dashboard可以查看一些信息:

[arthas@11100]$ dashboard
ID   NAME                          GROUP          PRIORITY  STATE    %CPU      DELTA_TIM TIME      INTERRUPT DAEMON
-1   C1 CompilerThread3            -              -1        -        0.0       0.000     0:0.468   false     true
5    Attach Listener               system         5         RUNNABLE 0.0       0.000     0:0.375   false     true
-1   C2 CompilerThread0            -              -1        -        0.0       0.000     0:0.234   false     true
14   DestroyJavaVM                 main           5         RUNNABLE 0.0       0.000     0:0.125   false     false
-1   C2 CompilerThread2            -              -1        -        0.0       0.000     0:0.125   false     true
30   arthas-NettyHttpTelnetBootstr system         5         RUNNABLE 0.0       0.000     0:0.109   false     true
-1   C2 CompilerThread1            -              -1        -        0.0       0.000     0:0.109   false     true
-1   VM Thread                     -              -1        -        0.0       0.000     0:0.093   false     true
23   arthas-NettyHttpTelnetBootstr system         5         RUNNABLE 0.0       0.000     0:0.031   false     true
-1   GC task thread#9 (ParallelGC) -              -1        -        0.0       0.000     0:0.031   false     true
31   arthas-command-execute        system         5         TIMED_WA 0.0       0.000     0:0.015   false     true
6    Monitor Ctrl-Break            main           5         RUNNABLE 0.0       0.000     0:0.015   false     true
-1   GC task thread#8 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#7 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#6 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#0 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#1 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#2 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#3 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#5 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#4 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
2    Reference Handler             system         10        WAITING  0.0       0.000     0:0.000   false     true
3    Finalizer                     system         8         WAITING  0.0       0.000     0:0.000   false     true
4    Signal Dispatcher             system         9         RUNNABLE 0.0       0.000     0:0.000   false     true
16   _jprofiler_sampler            system         10        TIMED_WA 0.0       0.000     0:0.000   false     true
15   _jprofiler_control_sampler    system         5         TIMED_WA 0.0       0.000     0:0.000   false     true
20   arthas-timer                  system         5         WAITING  0.0       0.000     0:0.000   false     true
24   arthas-NettyWebsocketTtyBoots system         5         RUNNABLE 0.0       0.000     0:0.000   false     true
25   arthas-NettyWebsocketTtyBoots system         5         RUNNABLE 0.0       0.000     0:0.000   false     true
Memory                    used    total    max     usage    GC
heap                      37M     146M     3609M   1.03%    gc.ps_scavenge.count          2
ps_eden_space             28M     63M      1332M   2.14%    gc.ps_scavenge.time(ms)       11
ps_survivor_space         0K      10752K   10752K  0.00%    gc.ps_marksweep.count         1
ps_old_gen                8M      72M      2707M   0.32%    gc.ps_marksweep.time(ms)      27
nonheap                   31M     32M      -1      97.18%
code_cache                5M      6M       240M    2.46%
metaspace                 23M     23M      -1      97.89%
compressed_class_space    2M      2M       1024M   0.26%
direct                    0K      0K       -       106.25%
mapped                    0K      0K       -       0.00%
Runtime
os.name                                                     Windows 10
os.version                                                  10.0
java.version                                                1.8.0_231
java.home                                                   D:\develop\jdk1.8.0_231_x64\jre
systemload.average                                          -1.00
processors                                                  12
timestamp/uptime                                            Thu Feb 04 22:24:08 CST 2021/2404s
[arthas@11100]$

退出则是quit

[arthas@11100]$ quit
PS C:\Users\songzeceng\Desktop\软件\02_软件>

我们也可以在启动jar包时就指明要连接的java进程pid:

PS D:\develop> jps
15488
9360 Jps
4900 Launcher
11100 ThreadDeadLock

PS D:\develop> java -jar .\arthas-boot.jar 11100
[INFO] arthas-boot version: 3.4.5
[INFO] Process 11100 already using port 3658
[INFO] Process 11100 already using port 8563
[INFO] arthas home: C:\Users\songzeceng\.arthas\lib\3.4.6\arthas
[INFO] The target process already listen port 3658, skip attach.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.
/  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'

wiki      https://arthas.aliyun.com/doc
tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html
version   3.4.6
pid       11100
time      2021-02-04 22:23:27

[arthas@11100]$

输入thread查看线程信息:

[arthas@11100]$ thread
Threads Total: 37, NEW: 0, RUNNABLE: 10, BLOCKED: 2, WAITING: 4, TIMED_WAITING: 4, TERMINATED: 0, Internal threads: 17
ID   NAME                          GROUP          PRIORITY  STATE    %CPU      DELTA_TIM TIME      INTERRUPT DAEMON
-1   C2 CompilerThread0            -              -1        -        7.8       0.015     0:0.250   false     true
-1   C1 CompilerThread3            -              -1        -        7.8       0.015     0:0.531   false     true
2    Reference Handler             system         10        WAITING  0.0       0.000     0:0.000   false     true
3    Finalizer                     system         8         WAITING  0.0       0.000     0:0.000   false     true
4    Signal Dispatcher             system         9         RUNNABLE 0.0       0.000     0:0.000   false     true
5    Attach Listener               system         5         RUNNABLE 0.0       0.000     0:0.375   false     true
16   _jprofiler_sampler            system         10        TIMED_WA 0.0       0.000     0:0.000   false     true
15   _jprofiler_control_sampler    system         5         TIMED_WA 0.0       0.000     0:0.000   false     true
20   arthas-timer                  system         5         WAITING  0.0       0.000     0:0.000   false     true
23   arthas-NettyHttpTelnetBootstr system         5         RUNNABLE 0.0       0.000     0:0.031   false     true
24   arthas-NettyWebsocketTtyBoots system         5         RUNNABLE 0.0       0.000     0:0.000   false     true
25   arthas-NettyWebsocketTtyBoots system         5         RUNNABLE 0.0       0.000     0:0.000   false     true
26   arthas-shell-server           system         5         TIMED_WA 0.0       0.000     0:0.000   false     true
27   arthas-session-manager        system         5         TIMED_WA 0.0       0.000     0:0.000   false     true
28   arthas-UserStat               system         5         WAITING  0.0       0.000     0:0.000   false     true
30   arthas-NettyHttpTelnetBootstr system         5         RUNNABLE 0.0       0.000     0:0.140   false     true
31   arthas-command-execute        system         5         RUNNABLE 0.0       0.000     0:0.015   false     true
33   arthas-NettyHttpTelnetBootstr system         5         RUNNABLE 0.0       0.000     0:0.015   false     true
6    Monitor Ctrl-Break            main           5         RUNNABLE 0.0       0.000     0:0.015   false     true
12   Thread-0                      main           5         BLOCKED  0.0       0.000     0:0.000   false     false
13   Thread-1                      main           5         BLOCKED  0.0       0.000     0:0.000   false     false
14   DestroyJavaVM                 main           5         RUNNABLE 0.0       0.000     0:0.125   false     false
-1   C2 CompilerThread2            -              -1        -        0.0       0.000     0:0.125   false     true
-1   GC task thread#8 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#7 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#6 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   VM Periodic Task Thread       -              -1        -        0.0       0.000     0:0.000   false     true
-1   GC task thread#0 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   Service Thread                -              -1        -        0.0       0.000     0:0.000   false     true
-1   C2 CompilerThread1            -              -1        -        0.0       0.000     0:0.109   false     true
-1   GC task thread#9 (ParallelGC) -              -1        -        0.0       0.000     0:0.031   false     true
-1   GC task thread#1 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   VM Thread                     -              -1        -        0.0       0.000     0:0.093   false     true
-1   GC task thread#2 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#3 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#5 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true
-1   GC task thread#4 (ParallelGC) -              -1        -        0.0       0.000     0:0.015   false     true

基础指令的使用

1)、help:查看帮助信息

[arthas@11100]$ help
NAME         DESCRIPTION
help         Display Arthas Help
keymap       Display all the available keymap for the specified connection.
sc           Search all the classes loaded by JVM
sm           Search the method of classes loaded by JVM
classloader  Show classloader info
jad          Decompile class
getstatic    Show the static field of a class
monitor      Monitor method execution statistics, e.g. total/success/failure count, average rt, fail rate, etc.
stack        Display the stack trace for the specified class and method
thread       Display thread info, thread stack
trace        Trace the execution time of specified method invocation.
watch        Display the input/output parameter, return object, and thrown exception of specified method invocation
tt           Time Tunnel
jvm          Display the target JVM information
perfcounter  Display the perf counter information.
ognl         Execute ognl expression.
mc           Memory compiler, compiles java files into bytecode and class files in memory.
redefine     Redefine classes. @see Instrumentation#redefineClasses(ClassDefinition...)
retransform  Retransform classes. @see Instrumentation#retransformClasses(Class...)
dashboard    Overview of target jvm's thread, memory, gc, vm, tomcat info.
dump         Dump class byte array from JVM
heapdump     Heap dump
options      View and change various Arthas options
cls          Clear the screen
reset        Reset all the enhanced classes
version      Display Arthas version
session      Display current session information
sysprop      Display, and change the system properties.
sysenv       Display the system env.
vmoption     Display, and update the vm diagnostic options.
logger       Print logger info, and update the logger level
history      Display command history
cat          Concatenate and print files
base64       Encode and decode using Base64 representation
echo         write arguments to the standard output
pwd          Return working directory name
mbean        Display the mbean information
grep         grep command for pipes.
tee          tee command for pipes.
profiler     Async Profiler. https://github.com/jvm-profiling-tools/async-profiler
stop         Stop/Shutdown Arthas server and exit the console.

2)、cat:打印文件内容

3)echo:打印参数

4)、grep:匹配查找

5)、tee:复制标准输出到指定文件

6)、pwd:打印当前目录

7)、cls:清屏

8)、session:查看当前会话信息

[arthas@11100]$ session
Name        Value
--------------------------------------------------
JAVA_PID    11100
SESSION_ID  c44a44d9-3c36-41f1-a160-833f771a2079

9)、reset:重置增强类

10)、quit:退出当前客户端

11)、stop:关闭服务端和所有客户端

dashboard与thread指令

dashboard:当前系统的实时数据面板

显示内容包括线程、线程组、优先级、状态、CPU占用率、内存信息、系统配置信息

面板信息会每隔一定时间自动输出一次,另外-n可以指定输出总次数(第一次不算,也就是-n 4会总共输出五次信息),-i指定输出的时间间隔(ms)

thread:当前JVM的线程堆栈信息

包括线程名、id、优先级、线程组、状态等

可以指定线程id来查看线程

[arthas@9876]$ thread 1
"main" Id=1 TIMED_WAITING
    at java.lang.Thread.sleep(Native Method)
    at OOMTest.main(OOMTest.java:10)

查看阻塞线程:

[arthas@9876]$ thread -b
No most blocking thread found!

设置输出延迟

[arthas@9876]$ thread -i 2000
Threads Total: 32, NEW: 0, RUNNABLE: 8, BLOCKED: 0, WAITING: 4, TIMED_WAITING: 3, TERMINATED: 0, Internal threads: 17
ID   NAME                          GROUP          PRIORITY  STATE    %CPU      DELTA_TIM TIME      INTERRUPT DAEMON
2    Reference Handler             system         10        WAITING  0.0       0.000     0:0.000   false     true
3    Finalizer                     system         8         WAITING  0.0       0.000     0:0.015   false     true
4    Signal Dispatcher             system         9         RUNNABLE 0.0       0.000     0:0.000   false     true
5    Attach Listener               system         5         RUNNABLE 0.0       0.000     0:0.015   false     true
13   arthas-timer                  system         5         WAITING  0.0       0.000     0:0.000   false     true
16   arthas-NettyHttpTelnetBootstr system         5         RUNNABLE 0.0       0.000     0:0.000   false     true
17   arthas-NettyWebsocketTtyBoots system         5         RUNNABLE 0.0       0.000     0:0.000   false     true
18   arthas-NettyWebsocketTtyBoots system         5         RUNNABLE 0.0       0.000     0:0.000   false     true
19   arthas-shell-server           system         5         TIMED_WA 0.0       0.000     0:0.000   false     true
20   arthas-session-manager        system         5         TIMED_WA 0.0       0.000     0:0.000   false     true
21   arthas-UserStat               system         5         WAITING  0.0       0.000     0:0.000   false     true
23   arthas-NettyHttpTelnetBootstr system         5         RUNNABLE 0.0       0.000     0:0.359   false     true
24   arthas-command-execute        system         5         RUNNABLE 0.0       0.000     0:0.015   false     true
1    main                          main           5         TIMED_WA 0.0       0.000     0:0.109   false     false
6    Monitor Ctrl-Break            main           5         RUNNABLE 0.0       0.000     0:0.000   false     true
-1   C2 CompilerThread2            -              -1        -        0.0       0.000     0:0.296   false     true
-1   GC task thread#8 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true
-1   GC task thread#7 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true
-1   GC task thread#6 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true
-1   VM Periodic Task Thread       -              -1        -        0.0       0.000     0:0.000   false     true
-1   GC task thread#0 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true
-1   C2 CompilerThread0            -              -1        -        0.0       0.000     0:0.250   false     true
-1   Service Thread                -              -1        -        0.0       0.000     0:0.000   false     true
-1   C2 CompilerThread1            -              -1        -        0.0       0.000     0:0.296   false     true
-1   GC task thread#9 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true
-1   GC task thread#1 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true
-1   C1 CompilerThread3            -              -1        -        0.0       0.000     0:0.515   false     true
-1   VM Thread                     -              -1        -        0.0       0.000     0:0.031   false     true
-1   GC task thread#2 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true
-1   GC task thread#3 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true
-1   GC task thread#5 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true
-1   GC task thread#4 (ParallelGC) -              -1        -        0.0       0.000     0:0.046   false     true

输出CPU占用率前n的线程

[arthas@9876]$ thread -n 2
"Reference Handler" Id=2 cpuUsage=0.0% deltaTime=0ms time=0ms WAITING on java.lang.ref.Reference$Lock@351395f0
    at java.lang.Object.wait(Native Method)
    -  waiting on java.lang.ref.Reference$Lock@351395f0
    at java.lang.Object.wait(Object.java:502)
    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"Finalizer" Id=3 cpuUsage=0.0% deltaTime=0ms time=15ms WAITING on java.lang.ref.ReferenceQueue$Lock@161dde44
    at java.lang.Object.wait(Native Method)
    -  waiting on java.lang.ref.ReferenceQueue$Lock@161dde44
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

sysprop命令和heapdump命令

sysprop:查看系统属性

heapdump:导出转储文件(--live可以指定只获取存活的对象)

[arthas@9876]$ heapdump  D:/arthas.hprof
Dumping heap to D:/arthas.hprof ...
Heap dump file created

生成结果:

sc与sm命令

sc命令:找到某个类

[arthas@9876]$ sc Picture
Picture
Affect(row-cnt:1) cost in 8 ms.

[arthas@9876]$ sc Pic*
Picture
Affect(row-cnt:1) cost in 2 ms.

输出类的详细信息:

[arthas@9876]$ sc Picture -d
class-info        Picture
code-source       /D:/develop/ideaWorkspace/JVMDemo/target/classes/
name              Picture
isInterface       false
isAnnotation      false
isEnum            false
isAnonymousClass  false
isArray           false
isLocalClass      false
isMemberClass     false
isPrimitive       false
isSynthetic       false
simple-name       Picture
modifier
annotation
interfaces
super-class       +-java.lang.Object
class-loader      +-sun.misc.Launcher$AppClassLoader@18b4aac2
                     +-sun.misc.Launcher$ExtClassLoader@5bc790a1
classLoaderHash   18b4aac2

Affect(row-cnt:1) cost in 7 ms.

查看类的属性:

[arthas@9876]$ sc Picture -d -f
class-info        Picture
code-source       /D:/develop/ideaWorkspace/JVMDemo/target/classes/
name              Picture
isInterface       false
isAnnotation      false
isEnum            false
isAnonymousClass  false
isArray           false
isLocalClass      false
isMemberClass     false
isPrimitive       false
isSynthetic       false
simple-name       Picture
modifier
annotation
interfaces
super-class       +-java.lang.Object
class-loader      +-sun.misc.Launcher$AppClassLoader@18b4aac2
                     +-sun.misc.Launcher$ExtClassLoader@5bc790a1
classLoaderHash   18b4aac2
fields            name     data
                   type     []
                   modifier private

Affect(row-cnt:1) cost in 3 ms.

sm命令:查看类的方法

[arthas@9876]$ sm Picture
Picture <init>([B)V
Affect(row-cnt:1) cost in 3 ms.

查看特定名称的方法:

[arthas@9876]$ sm Picture <init>
Picture <init>([B)V
Affect(row-cnt:1) cost in 3 ms.

查看方法详情:

[arthas@9876]$ sm Picture -d
declaring-class   Picture
constructor-name  <init>
modifier          public
annotation
parameters        []
exceptions
classLoaderHash   18b4aac2

Affect(row-cnt:1) cost in 3 ms.

jad、mc和classloader命令

jad命令:反编译某个类

[arthas@9876]$ jad Picture

ClassLoader:
+-sun.misc.Launcher$AppClassLoader@18b4aac2
  +-sun.misc.Launcher$ExtClassLoader@5bc790a1

Location:
/D:/develop/ideaWorkspace/JVMDemo/target/classes/

/*
* Decompiled with CFR.
*/
class Picture {
    private byte[] data;

    public Picture(byte[] data) {
        this.data = data;
    }
}

Affect(row-cnt:1) cost in 852 ms.

反编译某个方法:

[arthas@9876]$ jad java.lang.String compareTo

ClassLoader:

Location:

@Override
public int compareTo(String string) {
    int n = this.value.length;
    int n2 = string.value.length;
    int n3 = Math.min(n, n2);
    char[] arrc = this.value;
    char[] arrc2 = string.value;
    for (int i = 0; i < n3; ++i) {
        char c = arrc[i];
        char c2 = arrc2[i];
        if (c == c2) continue;
        return c - c2;
    }
    return n - n2;
}
@Override
public /* bridge */ /* synthetic */ int compareTo(Object object) {
    return this.compareTo((String)object);
}

Affect(row-cnt:2) cost in 737 ms.

mc命令:编译java文件成class文件

[arthas@9876]$ mc D:/develop/ideaWorkspace/JVMDemo/src/main/java/OOMTest.java
Memory compiler output:
D:\develop\ideaWorkspace\JVMDemo\OOMTest.class
D:\develop\ideaWorkspace\JVMDemo\Picture.class
Affect(row-cnt:2) cost in 699 ms.

之后我们可以进行class文件的热替换,内存中的类也随之替换:

[arthas@9876]$ redefine  D:/develop/ideaWorkspace/JVMDemo/target/classes/OOMTest.class
redefine success, size: 1, classes:
OOMTest

classloader命令:查看类加载器相关信息

[arthas@9876]$ classloader
name                                       numberOfInstances  loadedCountTotal
BootstrapClassLoader                       1                  2604
com.taobao.arthas.agent.ArthasClassloader  1                  2501
java.net.FactoryURLClassLoader             1                  869
sun.misc.Launcher$ExtClassLoader           1                  61
sun.reflect.DelegatingClassLoader          15                 15
sun.misc.Launcher$AppClassLoader           1                  8
Affect(row-cnt:6) cost in 3 ms.

以树形结构方式查看类加载器:

[arthas@9876]$ classloader -t
+-BootstrapClassLoader
+-sun.misc.Launcher$ExtClassLoader@5bc790a1
  +-com.taobao.arthas.agent.ArthasClassloader@efd5d92
  +-sun.misc.Launcher$AppClassLoader@18b4aac2
    +-java.net.FactoryURLClassLoader@460541f3
Affect(row-cnt:5) cost in 3 ms.

查看类加载器加载数量:

[arthas@9876]$ classloader -l
name                                               loadedCount  hash      parent
BootstrapClassLoader                               2604         null      null
com.taobao.arthas.agent.ArthasClassloader@efd5d92  2504         efd5d92   sun.misc.Launcher$ExtClassLoader@5bc790a1
java.net.FactoryURLClassLoader@460541f3            869          460541f3  sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2          8            18b4aac2  sun.misc.Launcher$ExtClassLoader@5bc790a1
sun.misc.Launcher$ExtClassLoader@5bc790a1          61           5bc790a1  null
Affect(row-cnt:5) cost in 2 ms.

查看某个hash对应的类加载器的情况:

[arthas@9876]$ classloader -c efd5d92
file:/C:/Users/songzeceng/.arthas/lib/3.4.6/arthas/arthas-core.jar

Affect(row-cnt:2) cost in 0 ms.

monitor、watch、trace、stack和tt命令

monitor命令:方法执行监控

[arthas@9876]$ monitor Picture <init>
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 342 ms, listenerId: 1

每2分钟刷新一次,可以通过-c参数设置,单位秒:

[arthas@9876]$ monitor Picture <init> -c 5
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 22 ms, listenerId: 2
timestamp         class                       method                     total    success   fail     avg-rt(  fail-rams)      te
-----------------------------------------------------------------------------------------------------------------------
2021-02-04 23:12   Picture                     <init>                     1        1         0        0.04     0.00%:27

timestamp         class                       method                     total    success   fail     avg-rt(  fail-rams)      te
-----------------------------------------------------------------------------------------------------------------------
2021-02-04 23:12  Picture                     <init>                     0        0         0        0.00     0.00%:32

watch命令:方法执行的数据监测

[arthas@9876]$ watch Picture <init> "{params,returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 21 ms, listenerId: 7
method=Picture.<init> location=AtExit
ts=2021-02-04 23:17:27; [cost=0.0805ms] result=@ArrayList[
    @Object[][
        @byte[][isEmpty=false;size=1203200],
    ],
    null,
]

此例中观察Picture类的构造方法,关注形参和返回值,设置最大嵌入深度为2

trace命令:方法内部调用路径

[arthas@9876]$ trace Picture <init>
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 31 ms, listenerId: 8
`---ts=2021-02-04 23:19:57;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@18b4aac2
    `---[0.0533ms] Picture:<init>()

输出内容有调用线程信息等

stack命令:方法被调用的路径

[arthas@9876]$ stack Picture <init>
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 23 ms, listenerId: 9
ts=2021-02-04 23:21:37;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@18b4aac2
    @Picture.<init>()
        at OOMTest.main(OOMTest.java:9)

输出了调用方法的线程和方法被调用的位置等信息

tt命令:方法执行数据的时空隧道

[arthas@9876]$ tt -t Picture <init>
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 24 ms, listenerId: 10
INDEX  TIMESTAMP          COST(ms  IS-RE  IS-EXP  OBJECT        CLASS                      METHOD
                           )        T
-----------------------------------------------------------------------------------------------------------------------
1000   2021-02-04 23:24:  0.0484   true   false   0x1d56ce6a    Picture                    <init>
        07

其他相关命令

1)、profiler:生成火焰图,此命令只能在Linux/Mac下使用

2)、options:arthas全局开关选项

JavaMissionControl

JMC从Oracle JDK 7u40之后就已经绑定在Oracle JDK中发布了,jdk11开始,其中的JFR开源。我们通过jmc.exe就可以启动jmc

点击界面中的火箭图标,就进入了主界面:

从左边选择一个JVM进程,双击MBean服务器就可以连接:

在弹出的两个会话框中分别选择是、完成,稍等片刻,就可以看到如下界面:

值得一提的是,jmc采用采样的方法采集数据,对进程的性能影响非常小

至于飞行记录器,双击启动即可

Btrace

Sun云计算开发平台的开源项目之一,是一个java平台的安全动态追踪工具,可以动态调整目标应用程序以注入跟踪代码

FlameGraphs火焰图

用来展示CPU在程序整个生命周期中时间的分配情况,详情参见文章:http://brendangregg.com/flamegraphs.html

结语

本文介绍了很多JVM图形化监控工具,比如jConsole、MAT等,挑一两款自己喜欢的去熟悉即可,下一篇文章将对JVM参数做一次总结

猜你喜欢

转载自blog.csdn.net/qq_37475168/article/details/115263132