【JVM进阶之路】九:性能监控工具-可视化工具篇

1,306 阅读5分钟

在前面已经学习了JVM性能监控的命令行工具,接下来学习JVM性能监控的命令行工具,通过可视化工具可以更直观地监控JVM性能、处理JVM相关问题。

1、JConsole

JConsole( Java Monitoring and Management Console),是一款基于 JMX( Java Manage-ment Extensions) 的可视化监视管理工具。

它的功能主要是对系统进行收集和参数调整,不仅可以用在虚拟机本身的管理上,还可以用于运行于虚拟机之上的软件中。

1.1、JConsole连接Java程序

JConsole程序位于%JAVA_HOME%bin目录下,直接通过命令启动。

JConsole启动和连接

在新建连接对话框中,罗列了所有的本地Java应用程序,选择需要连接的程序即可。

下面还有一个用于连接远程进程的文本框,输入正确的远程地址即可连接。

如果一个程序需要使用JConsole与那成连接,则需要在启动Java程序时,加上以下参数:

JAVA_OPTS="-Dfile.encoding=UTF-8" 
JAVA_OPTS="$JAVA_OPTS -Dlog.dir=$LOG_PATH" 
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=xxx.xxx.xxx.xxx(本机IP) -Dcom.sun.management.jmxremote" 
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=xx" 
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.local.only=false"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"

1.2、Java程序概况

使用JConsole连接了一个本地程序,在概述可以看到Java程序运行的概览信息,包括堆内存使用情况线程CPU使用情况四项信息的曲线图。

JConsole概览

1.3、内存监控

内存的作用相当于可视化的jstat命令,用于监视被收集器管理的虚拟机内存。

它不仅包含堆内存的整体信息,更细化到eden区、suvivior区、老年代的使用情况。

JConsole内存监控

为了更加清晰地查看内存地变化,运行下面一段程序,然后连接:

/**
 * VM参数: -Xms100m -Xmx100m -XX:+UseSerialGC
 */
public class JConcoleRAMMonitor {

    /***
     * 内存占位符对象,一个OOMObject大约占64KB
     */
    static class OOMObject {
        public byte[] placeholder = new byte[64 * 1024];
    }

    public static void fillHeap(int num) throws InterruptedException {
        List<OOMObject> list = new ArrayList<OOMObject>();
        for (int i = 0; i < num; i++) {
            // 稍作延时,令监视曲线的变化更加明显
            Thread.sleep(300);
            list.add(new OOMObject());
        }
        System.gc();
    }

    public static void main(String[] args) throws Exception {
        fillHeap(2000);
    }
}

这段代码的作用是以64KB/50ms的速度向Java堆中填充数据,一共填充1000次。

观察Eden区的运行趋势,发现呈折线。观察堆内存使用,发现以稍有曲折的状态向上增长。

Eden区内存变化状况

执行System.gc()之后,老年代的柱状图仍然显示峰值状态,最后程序会以堆内存溢出结束,这是因为空间未能回收——List<OOMObject>list对象一直存活, fillHeap()方法仍然没有退出,如果把 System.gc()移动到fillHeap()方法外调用就可以回收掉全部内存。

1.4、线程监控

JConcole还可以监控线程,相当于可视化的jstack命令。如图,JConcole显示了系统内的线程数量,并在屏幕下方显示了程序中所有的线程。单击线程名称,就可以查看线程的栈信息。

JConsole线程监控

使用JConsole还可以快速定位死锁问题。

这是一段会产生死锁的代码:

public class ThreadLockDemo {

    /**
     * 线程死锁等待演示
     */
    static class SynAddRunalbe implements Runnable {
        int a, b;

        public SynAddRunalbe(int a, int b) {
            this.a = a;
            this.b = b;
        }

        @Override
        public void run() {
            synchronized (Integer.valueOf(a)) {
                synchronized (Integer.valueOf(b)) {
                    System.out.println(a + b);
                }
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new SynAddRunalbe(1, 2)).start();
            new Thread(new SynAddRunalbe(2, 1)).start();
        }
    }
}

出现线程死锁以后,点击JConsole线程面板的检测到死锁按钮,将会看到线程的死锁信息。

线程死锁检测

可以看到线程Thread-199等待线程Thread-21持有的资源。

1.5、类加载情况

如图,类页面显示了已经装载的类数量。在详细信息栏中,还显示了已经卸载的类的数量。

类加载情况

1.6、虚拟机信息

VM摘要,JConsole显示了当前应用程序的运行环境,包括虚拟机类型、版本、堆信息以及虚拟机参数等。

VM 摘要

2、VisualVM

VisualVM(All-in-One Java Troubleshooting Tool)是功能最强大的运行监视和故障处理程序之一,曾经在很长一段时间内是Oracle官方主力发展的虚拟机故障处理工具。

相比一些第三方工具,VisualVM有一个很大的优点:不需要被监视的程序基于特殊Agent去运行,因此它的通用性很强,对应用程序实际性能的影响也较小,使得它可以直接应用在生产环境中。

2.1、VisualVM安装插件

在JDK6 Update7以后,VisualVM便作为JDK的一部分发布,它在%JAVA_HOME%bin 目录下,点击就可以启动。

VisualVM位置

VisualVM的精华之处在于它的插件。插件安装可以手动安装或者自动安装。

手动安装,从地址 visualvm.github.io/pluginscent… 下载载nbm包,点击“工具->插件->已下载”菜单,然后在弹出对话框中指定nbm包路径便可完成安装。

一般选择自动安装,点击工具-> 插件菜单,在可用插件里可以看到可安装的插件,按需安装即可。

VisualVM安装插件

VisualVM中概述,监视线程MBeans的功能与前面介绍的JConsole差别不大,这里就不在赘言。

2.2、生成、浏览堆转储快照

在VisualVM中生成堆转储快照文件有两种方式,可以执行下列任一操作:

  • 应用程序窗口中右键单击应用程序节点,然后选择堆Dump
  • 应用程序窗口中双击应用程序节点以打开应用程序标签,然后在“监视”标签中单击堆Dump

堆Dump

生成堆转储快照文件之后,该堆的应用程序下增加了一个以[heap-dump]开头的子节点。如果需要把堆转储快照保存或发送出去,就需要heapdump节点上右键选择“另存为”菜单,否则当VisualVM关闭时,生成的堆转储快照文件会被当作临时文件自动清理掉。要打开一个由已经存在的堆转储快照文件,通过文件菜单中的“装入”功能,选择磁盘上的文件即可。

VisualVM生成的堆转储快照

2.3、分析程序性能

要开始性能分析,先选择“CPU”和“内存”按钮中的一个,然后切换到应用程序中对程序进行操作,VisualVM会记录这段时间中应用程序执行过的所有方法。

VisualVM性能分析

如果是进行处理器执行时间分析,将会统计每个方法的执行次数、执行耗时;

VisualVM CPU分析

如果是内存分析,则会统计每个方法关联的对象数以及这些对象所占的空间。

VisualVM内存分析

等要分析的操作执行结束后,点击“停止”按钮结束监控过程。

2.4、BTrace动态日志跟踪

BTrace是个很有意思的插件,它可以在不停机的情况下,通过字节码注入动态监控系统的运行情况。

Btrace自动安装如下,到github的网络可能存在不稳定的问题,可以重试,或者手动安装

BTrace插件安装

在VisualVM中安装了BTrace插件后,在应用程序面板中右击要调试的程序,会出现“Trace Application…”菜单:

image-20210407231817248

点击将进入BTrace面板。这个面板看起来就像一个简单的Java程序开发环境:

image-20210407231851293

现在来尝试使用BTrace追踪正在运行的程序。

一段简单的Java代码:产生两个1000以内的随机整数,输出这两个数字相加的结果。

public class BTraceTest {
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) throws IOException {
        BTraceTest test = new BTraceTest();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        for (int i = 0; i < 10; i++) {
            reader.readLine();
            int a = (int) Math.round(Math.random() * 1000);
            int b = (int) Math.round(Math.random() * 1000);
            System.out.println(test.add(a, b));
        }
    }
}

运行程序,现在需要在不停止程序的情况下,监控程序中生成的两个随机数。在VisualVM中打开该程序的监视,在BTrace页 签填充TracingScript的内容,输入调试代码:

/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
	 @OnMethod(clazz = "cn.fighter3.test.BTraceTest", 
      method = "add", 
      location = @Location(Kind.RETURN)
    )
    public static void func(@Self cn.fighter3.test.BTraceTest instance, 
        int a, int b,
        @Return int result) {
        println("调用堆栈:");
        jstack();
        println(strcat("方法参数A:", str(a)));
        println(strcat("方法参数B:", str(b)));
        println(strcat("方法结果:", str(result)));
    }
}

点击start按钮,当程序运行时将会在Output面板输出调试信息:

image-20210407233504006

BTrace的用途很广泛,打印调用堆栈、参数、返回值只是它最基础的使用形式,更多应用可以查看官方仓库 github.com/btraceio/bt…

3、Java Mission Control

JMV最初是JRockit虚拟机提供的一款诊断工具。在Oracle JDK7 Update 40以后,它就绑定在Oracle JDK中发布。

JMC位置是%JAVA_HOME%/bin/jmc.exe,打开软件界面:

JMC主要界面

在左侧的“JVM浏览器”面板中自动显示了通过JDP协议(Java Discovery Protocol)找到的本机正在运行的HotSpot虚拟机进程。

3.1、MBean服务器

点击本地进程的MBean服务器

MBean服务器

可以看到,以飞行仪表的视图显示了Java堆使用率,CPU使用率和Live Set+Fragmentation。

3.2、飞行记录器(Flight Recorder)

飞行记录器是JMC提供的另一大功能,它通过记录程序在一段时间内的运行情况,将记录结果进行分析和展示,可以更进一步对系统的性能进行分析和诊断。

要使用JFR,程序启动需要带以下参数:

-XX:+UnlockCommercialFeatures  -XX:+FlightRecorder

连接加了相关参数启动的程序,启动飞行记录,进行一分钟的性能记录:

image-20210408000854241

记录结束后,JMC会自动打开刚才的记录:

image-20210408001133160

JFR提供的数据质量通常也要比其他工具通过代理形式采样获得或者从MBean中取得的数据高得多。以垃圾搜集为例,HotSpot的MBean中一般有各个分代大小、收集次数、时间、占用率等数据(根据收集器不同有所差别),这些都属于“结果”类的信息,而JFR中还可以看到内存中这段时间分配了哪些对象、哪些在TLAB中(或外部)分配、分配速率 和压力大小如何、分配归属的线程、收集时对象分代晋升的情况等。

4、第三方工具

以上三个都是JDK自带的性能监控工具,除此之外还有一些第三方的性能监控工具。

  • MAT

Java 堆内存分析工具。

  • GChisto

GC 日志分析工具。

  • GCViewer

GC 日志分析工具。

  • JProfiler

商用的性能分析利器。

  • arthas

阿里开源诊断工具。

  • async-profiler

Java 应用性能分析工具,开源、火焰图、跨平台。

这里只是简单罗列,就不再展开详细介绍。




参考:

【1】:周志明编著《深入理解Java虚拟机:JVM高级特性与最佳实践》

【2】:《实战JAVA虚拟机 JVM故障诊断与性能优化》

【3】:Jvm 系列(七):Jvm 调优-工具篇

【4】:给,你们想要的排查问题的可视化工具