JVM监控及诊断工具之命令行篇

概述

性能诊断是软件工程师在日常生活中需要经常面对的问题,在用户体验至上的今天,解决好应用的性能问题能带来非常大的收益。
Java作为最流行的编程语言之一,其应用的性能诊断能力一直受到业界的广泛关注。可能造成Java应用出现性能问题的因素非常多,例如线程控制,磁盘读写,数据库访问,网络I/O,垃圾收集等。想要定位这些问题,优秀的性能诊断工具必不可少。

jps:查看正在运行的JAVA进程

概览

package priv.user.jvm;

import java.util.Scanner;

/**
 * Description: 利用扫描器的阻塞状态验证jps的使用
 * Author:江洋大盗
 * Date:2021/3/11 21:08
 */
public class JPSTest {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        scanner.next();
    }
}

在这里插入图片描述

基本用法

jps [ options ] [ hostid ]

options参数:

  • -q:仅仅显示LVMID(local virtual machine id),即本地虚拟机唯一id,不显示主类名称。在这里插入图片描述

  • -l:输出应用程序的全类名或者如果进程是jar包,则输出jar完整路径在这里插入图片描述

  • -m:输出虚拟机进程启动时传递给主类main()的参数在这里插入图片描述在这里插入图片描述

  • -v:列出虚拟机进程启动时的JVM参数,比如:-Xms20m -Xmx50m在这里插入图片描述
    在这里插入图片描述

注:以上参数可以组合使用
在这里插入图片描述
在这里插入图片描述

补充:如果Java某进程关闭了默认开启的UsePerfData参数(即使用参数:-Xx:-UsePerfData),那么jps指令以及下面介绍的jstat指令将无法探知该Java进程。

jstat:查看JVM统计信息

基本情况

jstat(JVM Statistics Monitoring Tool),用于监视虚拟机各种运行状态信息的命令行工具,它可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集等信息。
在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具,常用于检测垃圾回收问题以及内存泄漏问题。

jstat -< option > [ -t ] [ -h < lines > ] < vmid > [ < interval > [ < count > ] ]

查看命令相关参数:
jstat -h 或 jstat -help

interval参数

用于指定输出统计数据的周期,单位为毫秒,即查询间隔,不指定interval时默认打印一次
在这里插入图片描述

count参数

用于指定查询的总次数

在这里插入图片描述

-t参数

可以在输出信息上加一个Timestamp列,显示程序的运行时间,单位秒
在这里插入图片描述

-h参数

可以在周期性数据输出时,在输出多少行数据后输出一次表头信息
在这里插入图片描述

options参数

选项option参数可以由以下值构成:

类装载相关的

  • -class:显示ClassLoader的相关信息,类的装载、卸载数量、总空间、类装载所消耗的时间

垃圾回收相关的:

  • -gc:显示与gc相关的堆信息,包括Eden区,两个Survivor区,老年代,永久代等容量、已用空间、GC时间合计等信息。
  • -gccapacity:显示内容基本与 -gc 相同,但输出主要关注Java堆各个区域使用到的最大、最小的空间。
  • -gcutil:显示内容基本与 -gc 相同,但输出主要关注已用空间占总空间的百分比。
  • -gccause:显示内容与 -gcutil 相同,但是会额外输出导致最后一次或当前正在发生的GC的产生的原因。
  • -gcnew:显示新生代GC状况
  • -gcnewcapacity:显示内容与 -gcnew 基本相同,输出主要关注使用到的最大、最小空间。
  • -gcold:显示老年代GC状况

JIT相关的

  • -compiler:显示JIT编译器编译过的方法、耗时等信息
  • -printcompilation:输出已经被JIT编译过的方法

jinfo:实时查看和修改JVM参数

基本情况

查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数
在很多情况下,Java应用程序不会指定所有的Java虚拟机参数,而此时,开发人员可能不知道某一个具体的Java虚拟机参数的默认值。在这种情况下,可能需要查找文档获取某个参数的默认值,这个查找过程可能是非常艰难的。但有了jinfo工具,开发人员可以很方便的找到Java虚拟机参数的当前值。

基本用法

jinfo [ options ] pid
说明 ,java进程id必须要加上

【options参数】

选项参数说明
no options输出全部的参数和系统属性
-flag name输出对应名称的参数
-flag [±]name开启或者关闭对应名称的参数,只有被标记为manageable的参数才可以被动态修改
-flag name=value设定对应名称的参数
-flags输出全部参数
-sysprops输出系统属性

查看

  • jinfo -sysprops PID:可以查看由System.getProperties()获取的参数在这里插入图片描述

  • jinfo -flags PID:查看曾经赋过值的一些参数在这里插入图片描述

  • jinfo -flag 具体参数 PID:查看某个Java进程的具体参数的值
    在这里插入图片描述

修改

可以查看被标记为manageable的参数
Linux系统下的指令
java -XX:+PrintFlagsFinal -version | grep manageable

  • 针对boolean类型的 jinfo -flag [±]具体参数 PID在这里插入图片描述

  • 针对非boolean类型的 jinfo -flag 具体参数=具体参数值 PID
    在这里插入图片描述

拓展

  • java:-XX:+PrintFlagsInital :查看JVM启动时的初始值
  • java:-XX:+PrintFlagsFinal:查看JVM参数的最终值
  • java:-XX:+PrintCommandLineFlags:查看哪些已经被用户或者JVM设置过的详细参数XX的名称和值

jmap:到处内存映像文件和内存使用情况

基本情况

jmap(JVM Memory Map):作用一方面是获取dump文件(堆转储快照文件,二进制文件),它还可以获取目标Java进程的内存相关信息,包括Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。

开发人员可以在控制台中输入命令“jmap -help”查阅jmap工具的具体使用方式和一些标准选项配置。

官方帮助文档点这里

基本语法

它的基本使用语法为:

  • jmap [option] < pid >
  • jmap [option] <executable < core >
  • jmap [option] [ server_id@ ]< remote server IP or hostname>

其中option包括:

选项作用
-dump生成Java堆转储快照:dump文件,特别的: -dump:live只保存堆中的存活对象
-heap输出整个堆空间的详细信息,包括GC的使用、堆配置信息,以及内存的使用信息等
-histo输出堆空间中对象的统计信息,包括类、实例数量和合计容量 ,特别的: -histo:live只统计堆中的存活对象
-permstat以ClassLoader为统计口径输出永久代的内存状态信息,仅linux/solaris平台有效
-finalizerinfo显示在F-Queue中等待Finalizer线程执行finalize方法的对象,仅linux/solaris平台有效
-F当虚拟机进程对-dump选项没有任何响应时,强制执行生成dump文件,仅linux/solaris平台有效
-h / -helpjmap工具使用的帮助命令
-J< flag >传递参数给jmap启动的jum

说明:这些参数和linux下输入显示的命令多少会有不同,包括也受jdk版本的影响。

导出内存映像文件(主要作用)

一般来说,使用jmap指令生成dump文件的操作算得上是最常用的jmap命令之一,将堆中所有存活对象导出至一个文件之中。

Heap Dump又叫做堆存储文件,指一个Java进程在某个时间点的内存快照。Heap Dump在触发内存快照的时候会保存此刻的信息如下:

  • All 0bjects:Class,fields ,primitive values and references
  • All Classes:ClassLoader ,name , super class ,static fields.
  • Garbage collection Roots:Objects defined to be reachable by the JVM.
  • Thread Stacks and Local Variables:The call-stacks of threads at the moment of the snapshot,and per-frameinformation about local objects

说明:

  1. 通常在写Heap Dump文件前会触发一次Full Gc,所以 heap dump 文件里保存的都是FullGC后留下的对象信息。
  2. 由于生成dump文件比较耗时,因此大家需要耐心等待,尤其是大内存镜像生成dump文件则需要耗费更长的时间来完成。

手动的方式

测试代码:

package priv.user.jvm;

import java.util.ArrayList;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/3/15 21:53
 * -Xms60m -Xmx60m -XX:SurvivorRatio=8
 */
public class JMAPTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            byte[] bytes = new byte[1024*100];//100kb
            list.add(bytes);
            try {
                Thread.sleep(120);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

jmap -dump:format=b,file=< filename.hprof >< pid >

这种方式会记录堆中所有的对象
在这里插入图片描述
在这里插入图片描述

jmap -dump:live,format=b,file=< filename.hprof > < pid >

这种方式会记录堆中所有的存活对象
在这里插入图片描述
在这里插入图片描述

自动的方式

当程序发生OOM退出系统时,一些瞬时信息都随着程序的终止而消失,而重现oOM问题往往比较困难或者耗时。此时若能在OOM时,自动导出dump文件就显得非常迫切。

这里介绍一种比较常用的取得堆快照文件的方法,即使用:
-XX:+HeapDumpOnOutOfMemoryError:在程序发生OOM时,导出应用程序的当前堆快照。
-XX:HeapDumpPath:可以指定堆快照的保存位置。

比如:
-Xmx10m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\m.hprof

在这里插入图片描述

显示堆内存相关信息

jmap -heap pid

查看堆中当前时刻的内存使用情况

jmap -histo pid

分析堆中的对象内容

其他作用

jmap -permstat pid

查看系统的ClassLoader信息

jmap -finalizerinfo

查看堆积在finalizer队列中的对象

小结

由于jmap将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由jmap导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。

举个例子,假设在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么: live选项将无法探知到这些对象。

另外,如果某个线程长时间无法跑到安全点,jmap将一直等下去。
与前面讲的jstat则不同,垃圾回收器会主动将jstat所需要的摘要数据保存至固定位置之中,而jstat只需直接读取即可。

jhat:JDK自带的堆分析工具

基本情况

jhat(JVM Heap Analysis Tool):

Sun JDK提供的jhat命令与jmap命令搭配使用,用于分析jmap生成的heap dump文件(堆转储快照)。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,用户可以在浏览器中查看分析结果(分析虚拟机转储快照信息)。

使用了jhat命令,就启动了一个http服务,端口是7000,即http:/ /localhost:7000/,就可以在浏览器里分析。

说明:jhat命令在JDK9、JDK10中已经被删除,官方建议用VisualVM代替。
在这里插入图片描述
在这里插入图片描述

基本语法

它的基本使用语法为:
jhat [option] [ dumpfile ]

option参数:

参数作用
option参数:-stack false/true关闭
option参数: -refs false/true关闭
option参数: -port port-numberServer的端口号,默认7000
option参数: -exclude exclude-file执行对象查询时需要排除的数据成员
option参数: -baseline exclude-file指定一个基准堆转储
option参数: -debug int设置debug级别
option参数: -version启动后显示版本信息就退出
option参数: -J< flag >传入启动参数,比如-J-Xmx512m

jstack:打印JVM中线程快照

基本情况

jstack(JVM Stack Trace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟踪)。线程快照就是当前虚拟机内指定进程的每一条线程正在执行的方法堆栈的集合。

生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用jstack显示各个线程调用的堆栈情况。

官方帮助文档:

在thread dump中,要留意下面几种状态:

  • 死锁,Deadlock(重点关注)
  • 等待资源,waiting on condition(重点关注)
  • 等待获取监视器,Waiting on monitor entry(重点关注)
  • 阻塞,Blocked(重点关注)
  • 执行中,Runnable
  • 暂停,Suspended
  • 对象等待中,0bject.wait()或 TIMED_WAITING
  • 停止,Parked

死锁测试代码

package priv.user.jvm;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/3/16 10:02
 */
public class ThreadDeadLock {
    public static void main(String[] args) {
        StringBuilder s1 = new StringBuilder();
        StringBuilder s2 = new StringBuilder();

        new Thread(()->{
            synchronized (s1){
               s1.append("a");
               s2.append("1");

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

                synchronized (s2){
                    s1.append("b");
                    s2.append("2");

                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }).start();

        new Thread(()->{
            synchronized (s2){
                s1.append("c");
                s2.append("3");

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

                synchronized (s1){
                    s1.append("d");
                    s2.append("4");

                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }).start();
    }
}

在这里插入图片描述
线程等待测试代码

package priv.user.jvm;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/3/16 10:20
 */
public class ThreadSleep {
    public static void main(String[] args) {
        System.out.println("hello world");
        try {
            Thread.sleep(1000 * 60 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
线程同步代码

package priv.user.jvm;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/3/16 10:24
 */
public class ThreadSynTest {
    public static void main(String[] args) {
        GetNum num = new GetNum();
        Thread t1 = new Thread(num);
        Thread t2 = new Thread(num);

        t1.setName("线程1");
        t2.setName("线程2");

        t1.start();
        t2.start();
    }
}

class GetNum implements Runnable {
    private int num = 1;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (num <= 100) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    num++;
                } else {
                    break;
                }
            }
        }
    }
}

在这里插入图片描述

基本用法

option参数

参数作用
-F当正常输出的请求不被响应时,强制输出线程堆栈
-l除堆栈外,显示关于锁的附加信息
-m如果调用到本地方法的话,可以显示C/C++的堆栈
-h帮助操作

补充:
Java的Thread类中有一个方法可以输出当前的线程信息,不过效果不如jstack明显:

package priv.user.jvm;

import java.util.Map;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/3/16 10:46
 */
public class AllThreadTrace {
    public static void main(String[] args) {
        Map<Thread, StackTraceElement[]> all = Thread.getAllStackTraces();
        for (Map.Entry<Thread, StackTraceElement[]> entry : all.entrySet()) {
            Thread t = entry.getKey();
            StackTraceElement[] v = entry.getValue();

            System.out.println("【Thread name:" + t.getName() + "】");
            for (StackTraceElement s : v) {
                System.out.println("\t" + s.toString());
            }
        }
    }
}

jcmd:多功能的命令行

基本情况

在JDK 1.7以后,新增了一个命令行工具jcmd。

它是一个多功能的工具,可以用来实现前面除了jstat之外所有命令的功能。比如:用它来导出堆、内存使用、查看Java进程、导出线程信息、执行GC、JVM运行时间等。

官方帮助文档

jcmd拥有jmap的大部分功能,并且在Oracle的官方网站上也推荐使用jcmd命令代jmap命令

基本用法

  • jcmd -l :列出所有的JVM进程
  • jcmd pid help:针对指定的进程,列出支持的所有命令
  • jcmd pid 具体命令:显示指定进程的指令命令的数据

在这里插入图片描述

在这里插入图片描述

jstatd:远程主机信息收集

之前的指令只涉及到监控本机的Java应用程序,而在这些工具中,一些监控工具也支持对远程计算机的监控(如jps、jstat)。为了启用远程监控,则需要配合使用jstatd 工具。

命令jstatd是一个RMI服务端程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信。jstatd服务器将本机的Java应用程序信息传递到远程计算机。

在这里插入图片描述

结语

除了奋斗,我们别无选择

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值