其他分享
首页 > 其他分享> > 一些JDK自带的性能分析利器

一些JDK自带的性能分析利器

作者:互联网

有时候碰到服务器CPU飙升或者程序卡死之类的问题,一般都不太好定位。这类bug一般都隐藏的比较深并且还可能是偶发性的,比较棘手。

对于此类问题,一般我们都有固定的分析流程。借助于JDK自带的一些分析工具,比如jstack、jmap、jstat一类的命令行工具,除此之外,还有jconsole、mat、jvisualvm这些图形界面分析工具。

这篇文章基于JDK8,操作系统是macOS 12.0.1

1、一些命令行分析工具

这些命令行分析工具都在jdk/bin目录下

image-20220316160402785-7417844.png

解压jdk/lib/tool.jar可以得到上述工具的class文件

image-20220316160755917-7418077.png

1.1 jps - JVM Process Status Tool

作用:列出正在运行的虚拟机进程,并显示虚拟机执行主类名称以及这些进程的本地虚拟机唯一ID。

image-20220316193716333-7430637.png

第一个参数说明:

[-mlvV]可以任意组合使用。

第二个参数说明:

1.2 jstat - JVM Statistics Monitoring Tool

作用:监视虚拟机各种运行状态信息,可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

命令格式:jstat -

参数说明

第一个参数:option,代表用户希望查询的虚拟机信息,主要分为3类:类装载、垃圾收集和运行期编译情况。具体选项如下:

第二个参数:vmid。如果是本地虚拟机进程,那么vmid和本地虚拟机唯一ID是一致的。如果是远程虚拟机进程,那么vmid的格式是:protocol:lvmid[@hostname[:port]/servername]

第三个参数:interval。采样间隔,单位为s或ms,默认单位是ms,必须为整数。指定该参数,jstat命令将在每个间隔产生输出。

第四个参数:count。要显示的样本数。

import java.io.IOException;

/**
 * -class:
 *  Loaded:已加载的类数量。
 *  Bytes:已加载的kb数。
 *  Unloaded:卸载的类数量。
 *  Bytes:卸载的kb数。
 *  Time:执行类加载和卸载的耗时。
 *
 * -compiler:
 *  Compiled:执行的编译任务数。
 *  Failed:编译失败的任务数。
 *  Invalid:无效的编译任务数。
 *  Time:执行编译任务所花费的时间。
 *  FailedType:失败的编译类型。
 *  FailedMethod:失败的编译类名和方法。
 */
public class Demo1_jstat {
    public static void main(String[] args) throws IOException {
        System.out.println("jstat");
        System.in.read();
    }
}

启动上面的demo后

import java.io.IOException;

/**
 * -gc: 垃圾收集的堆统计信息
 *  S0C:s0区的容量(kb)
 *  S1C:s1区的容量(kb)
 *  S0U:s0区的使用大小(kb)
 *  S1U:s1区的使用大学(kb)
 *  EC:eden区的容量(kb)
 *  EU:eden区的使用大小(kb)
 *  OC:老年代容量(kb)
 *  OU:老年代的使用大小(kb)
 *  MC:元空间的容量(kb)
 *  MU:元空间的使用大小(kb)
 *  CCSC:压缩的类空间容量(kb)
 *  CCSU:使用的压缩类空间大小(kb)
 *  YGC:新生代垃圾收集次数
 *  YGCT:新生代垃圾回收时间
 *  FGC:full gc收集次数
 *  FGCT:full gc垃圾回收时间
 *  GCT:总垃圾收集时间
 *
 * -gcutil:垃圾收集统计信息
 *  S0:s0区的使用率
 *  S1:s1区的使用率
 *  E:eden区使用率
 *  O:老年代使用率
 *  M:元空间使用率
 *  CCS:压缩类空间使用率
 *  YGC:新生代gc次数
 *  YGCT:新生代gc耗费时间
 *  FGC:full gc次数
 *  FGCT:full gc耗费时间
 *  GCT:总垃圾收集时间
 */
public class Demo2_jstat {
    // -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc
    public static void main(String[] args) throws IOException {
        final int _1MB = 1024 * 1024;
        // 申请2M的空间
        byte[] b1 = new byte[2 * _1MB];
        System.out.println("1111");
        System.in.read();

        // 申请2M的空间
        byte[] b2 = new byte[2 * _1MB];
        System.out.println("2222");
        System.in.read();

        // 申请2M的空间
        byte[] b3 = new byte[2 * _1MB];
        System.out.println("3333");
        System.in.read();
    }
}

启动上面的程序之前,先指定一些jvm参数

-Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc

image-20220316211331049-7436412.png

然后启动demo

image-20220316211628805-7436590.png

刚启动时,程序会申请2M的数组,可以看到eden区的使用率是62.37%,ygc的值为0。

让程序申请第二个2M的数组,之后再查看堆内存信息

image-20220316211756657-7436677.png

可以发现eden区的使用率达到了87.37%。

让程序申请第三个2M的数组,发现控制台输出提示

image-20220316211928010-7436769.png

再查看堆内存信息

image-20220316211957942-7436799.png

发现eden区的使用率降到了26.71,s1区使用率时70.80%,老年代的使用率是40%即4096kb也就是4M空间大小。同时ygc的次数是1,说明进行了一次ygc,并且把大对象放进了老年代中。

1.3 jinfo - Configuration Info For Java

作用:实时的查看和调整虚拟机各项参数。

命令格式:jinfo [option]

参数说明

第一个参数:option。

命令演示

在我电脑上使用这个命令会报错,据说是macOS的一个bug,需要升级到jdk9,懒得升了。bug链接

image-20220316233053701-7444655.png

1.4 jmap - Memory Map For Java

作用:一个多功能的命令,它可以生成Java程序的dump文件,也可以查看堆内对象的信息、classloader的信息和finalizer队列。

命令格式:jmap [option]

参数解释

第一个参数:option

命令演示

在macOS上使用这个命令同样也会报错。但某些命令还是可以的,比如dump二进制文件。

1.5 jhat - JVM Heap Dump Browser

作用:与jmap搭配使用,用来分析jmap生成的堆转储文件。jhat内置了一个微型的http/html服务器,生成dump文件的分析结果后,可以在浏览器中查看。

不过这个工具一般比较少使用,一是因为功能比较简陋,VisualVM和MAT等工具完全能够替代它。二是因为我们一般不会在生产服务器上直接去dump二进制文件,并且分析二进制文件是一个比较耗时的工作,所以就没必要使用命令行工具了。

命令格式:jhat [options] 堆转储文件

参数解释

第一个参数:option

第二个参数:堆转储文件。

命令演示

我们可以先生成一个二进制文件。

image-20220317201604307-7519365.png

然后使用jhat命令进行分析

image-20220317202140820-7519702.png

浏览器访问8888端口

image-20220317202228672-7519750.png

1.6 jstack - Stack Trace For Java

作用:查看或导出Java应用程序中线程堆栈信息。

线程快照是当前Java虚拟机内每一条线程正在执行的方法堆栈的集合。生成线程快照的主要目的是定位线程出现长时间停顿的原因,比如线程死锁、死循环、长时间等待外部资源等。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。如果Java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而轻松地知道Java程序是如何崩溃和在程序何处发生问题。另外jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack信息。

命令格式:jstack [options]

参数说明

第一个参数:options

命令演示:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo5_jstack {
    public static void main(String[] args) {
        System.out.println("start");
        test1();
      	// test2();
        System.out.println("end");
    }

    // 测试死循环
    private static void test1() {
        while (true) {}
    }

    // 测试死锁
    private static void test2() {
        Lock lock1 = new ReentrantLock();
        Lock lock2 = new ReentrantLock();

        new Thread(() -> {
            try {
                lock1.lock();
                Thread.sleep(100);
                lock2.lock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread1").start();

        new Thread(() -> {
            try {
                lock2.lock();
                Thread.sleep(100);
                lock1.lock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread2").start();
    }
}

测试死循环

启动程序后,top命令查看进程状态

image-20220317215050067-7525051.png

Linux系统中可以使用top -Hp 50242命令查看进程下的线程信息,但在macOS上不支持这个命令。我也没找到怎么查看macOS里进程下所有线程的方式==。

一般在Linux上的步骤就是下面这几步:

(1)top查看哪个进程cpu最高。

(2)top -Hp pid查看进程下面哪个线程cpu最高。

(3)jstack -l pid打印出进程的堆栈信息,然后将占有cpu最高的线程id转换为16进制,将这个16进制在堆栈信息中查询它的位置,一般都能定位到具体的代码位置。

image-20220319223044483-7700245.png

测试死锁

调用test2方法,然后启动程序,使用jstack -l pid命令能够打印出死锁信息。

image-20220319223352905-7700434.png

2、一些可视化分析工具

2.1 jConsole

使用jConsole可以查看程序的堆内存使用量、线程信息、CPU使用信息等。

在控制台输入jconsole命令,选择我们本地的程序

image-20220319225123482-7701484.png

进入后就能看到一些基本信息了

image-20220319225234676-7701555.png

在内存模块,我们可以查看新生代、老年代、元空间等区域的使用率

image-20220319225322645-7701603.png

在线程模块,我们能看到该进程下的所有线程,同时还能检测死锁

image-20220319225553294.png

image-20220319225626134-7701787.png

在VM概要模块,则可以看到本机的一些JVM信息。

2.2 Visual VM

作用:是到目前为止随JDK发布的功能最强大的运行监视和故障处理程序。官方在VisualVM的软件说明中写上了“All-in-One”的描述,说明它除了运行监视、故障处理外,还提供了很多其他方面的功能。如性能分析,VisualVM的性能分析甚至比很多专业的收费工具都好用,而且VisualVM不需要被监视的程序基于特殊的运行,因此它对应用程序的实际性能的影响很小,使得它可以直接应用在生产环境中。

VisualVM基于NetBeans平台开发,因此它一开始就具备了插件扩展功能的特性,通过插件扩展支持,VisualVM可以做到:

使用:在控制台输入jvisualvm执行即可。进入后主页还会有一些文档,十分贴心。

image-20220320101903777-7742745.png

在工具栏可以进行插件的安装

image-20220320102033691-7742835.png

可以看到左侧可以选择本地进程或者远程的进程,选择我们的目标程序,顶部有概述、监视、线程、抽样器、Profiler几个选项。

image-20220320102219173-7742940.png

监视页面和jconsole的也有点像,不过在visualvm中可以直接进行堆dump文件分析

image-20220320102604420-7743165.png

在线程页面,还可以检测程序的死锁,进行线程dump的分析

image-20220320102722247-7743243.png

还有很多的功能大家可以一一去看看。以前还不知道JDK自带了这么多性能分析利器啊,以后遇到一些性能问题可以尝试使用一下上面的工具,也不需要额外安装。

文章首发于我的公众号【秃头哥编程】,欢迎大家关注。

标签:kb,java,JDK,虚拟机,信息,利器,参数,线程,自带
来源: https://www.cnblogs.com/codetiger/p/16029551.html