JVM下篇(二、JVM监控及诊断工具-命令行篇)

JVM_JVM监控及诊断工具-命令行篇

提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!!



二、JVM监控及诊断工具-命令行篇

1. 概述

性能诊断是软件工程师在日常工作中需要面对和解决的问题,在用户体验至上的今天,解决好应用的性能问题能带来非常大的收益。

Java作为最流行的编程语言之一,其应用性能诊断一直受到业内广泛关注,可能造成Java应用出现性能问题的因素非常多,例如:线程控制、磁盘读写、数据库访问、网络IO、垃圾收集等,想要定位这些问题,一款优秀的性能诊断工具必不可少。

体会一:使用数据说明问题,使用知识分析问题,使用工具处理问题。
体会二:无监控,不调优!

简单命令行工具

在我们刚接触java学习的时候,大家肯定最先了解的两个命令就是javac, java,那么除此之外,还有没有其他的命令可以供我们使用呢?我们进入到安装jdk的bin目录,发现还有一系列辅助工具。这些辅助工具用来获取目标 JVM 不同方面、不同层次的信息,帮助开发人员很好地解决Java应用程序的一些疑难杂症。

在这里插入图片描述

2. jps:查看正在运行的java进程

2.1 jps(Java Process Status)

显示指定系统内所有HotSpot虚拟机进程(查看虚拟机进程信息),可用于查询正在执行的虚拟机进程。

说明:对于本地虚拟机进程来说,进程的本地虚拟机ID与操作系统的进程ID是一致的,是唯一的。

Linux/Mac查看所有进程命令:ps aux(过滤进程号: ps aux | grep 进程号)

2.2 基本语法

2.2.1 jps [options] [hostid]

或者通过jps -help查看

在这里插入图片描述

2.2.2 options参数
  • 不加参数:

在这里插入图片描述

  • -q:仅仅显示LVMID(local virtual machine id),即本地虚拟机唯一id,不显示主类的名称等

在这里插入图片描述

  • -l:输出应用程序主类的全类名或如果进程执行的是jar包,则输出jar包的完成路径(l:可以理解为long

在这里插入图片描述

  • -m:输出虚拟机进程启动时传递给主类main()的参数(m:可以理解为main

在这里插入图片描述

  • -v:列出虚拟机进程启动时的JVM参数

在这里插入图片描述

以上参数可以组合使用。

补充:

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

2.2.3 hostid 参数(一般不使用)

RMI注册表中注册的主机名。
如果想要远程监控主机上的java程序,需要安装jstatd。

3. jstat:查看JVM统计信息

3.1 jstat(JVM Statistics Monitoring Tool)

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

在没有GUI图形界面,只提供纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄露问题

3.2 基本语法

3.2.1 基本使用语法

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

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

vmid:java进程id

在这里插入图片描述

3.2.2 option参数
  1. 类装载相关
  • -class:显示ClassLoader的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等
    在这里插入图片描述
  1. 垃圾回收相关

测试代码

package jvm.classloader.jstat;

import java.util.ArrayList;

public class GCTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        for (int i = 0; i < 1000; i++) {
            byte[] arr = new byte[1024 * 100];//100kb
            list.add(arr);
            try {
                Thread.sleep(120);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

JVM参数

-Xms60m -Xmx60m -XX:SurvivorRatio=8

相关列描述:

  1. 新生代相关
  • S0C是第一个幸存者区的大小(字节)
  • S1C是第二个幸存者区的大小(字节)
  • S0U是第一个幸存者区己使用的大小(字节)
  • S1U是第二个幸存者区己使用的大小(字节)
  • EC是Eden空间的大小(字节)
  • EU是Eden空间己使用大小(字节)
  1. 老年代相关
  • OC是老年代的大小(字节)
  • OU是老年代己使用的大小(字节)
  1. 方法区(元空问)相关
  • MC是方法区的大小
  • MU是方法区己使用的大小
  • CCSC是压缩类空间的大小
  • CCSU是压缩类空间已使用的大小
  1. 其它
  • YGC是指从应用程序启动到采样时young gc次数
  • YGCT是指从应用程序启动到采样时young gc消耗的时间(秒)
  • FGC是指从应用程序启动到采样时full gc次数
  • FGCT是指从应用程序启动到采样时full gc消耗的时间(秒)
  • GCT是指从应用程序启动到采样时gc的总时间
  • -gc:显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。

在这里插入图片描述

  • -gccapacity:显示内容与-gc基本相同,但是输出主要关注Java堆各个区域使用到的最大、最小空间。

在这里插入图片描述

  • -gcutil:显示内容与-gc基本相同,但是输出主要关注已使用空间占总空间的百分比。

在这里插入图片描述

  • -gccause:与-gcutil功能一样,但是会额外输出导致最后一次或当前正在发生的GC产生的原因。

在这里插入图片描述

  • -gcnew:显示新生代的GC状况。

在这里插入图片描述

  • -gcnewcapacity:显示内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间。

在这里插入图片描述

  • -gcold:显示老年代的GC状况。

在这里插入图片描述

  • -gcoldcapacity:显示内容与-gcold基本相同,输出主要关注使用到的最大、最小空间。

在这里插入图片描述

  • -gcpermcapacity(或-gcmetacapacity):显示永久代使用到的最大、最小空间。

在这里插入图片描述

  1. JIT相关
  • -compiler:显示JIT编译器编译过的方法、耗时等信息。

在这里插入图片描述

  • -printcompilation:输出已经被JIT编译的方法

在这里插入图片描述

3.2.3 interval参数

用于指定输出统计数据的周期,单位是毫秒。即:查询间隔

3.2.4 count参数

用于指定查询的总次数

3.2.5 -t 参数

可以在输出信息前加上一个Timestamp列,显示程序的运行时间。单位:秒

在这里插入图片描述

经验:

我们可以比较Java进程启动时间以及总GC时间(GCT列),或者两次测量的间隔时间以及总GC时间的增量,来得出GC时间占运行时间的比例。

如果该比例超过20%,则说明目前堆压力较大,如果该比例超过90%,则说明堆里几乎没有可用空间,随时都可能抛出OOM异常。

3.2.6 -h 参数

可以在周期性数据输出时,输出多少行数据后输出一个表头信息

在这里插入图片描述

3.2.7 补充

jstat还可以用来判断是否出现内存泄露。

  • 第一步:在长时间运行的Java程序中,我们可以运行jstat命令连续获取多行性能数据,并取这几行数据中的OU列(即已占用的老年代内存)的最小值。

  • 第二步:然后,我们每隔一段较长时间重复一次上述操作,来获取多组OU最小值。如果这些值呈上涨趋势,则说明该Java程序的老年代内存已使用量在不断上涨,这意味着无法回收的对象在不断增加,因此很有可能存在内存泄露。

4. jinfo:实时查看和修改JVM配置参数

4.1 基本情况

jinfo(Configuration Info for Java)

查看虚拟机配置参数信息,也可以用于调整虚拟机的配置参数。

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

4.2 基本语法

jinfo [options] pid(说明:java进程id必须加上)

在这里插入图片描述

options参数描述:

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

参考文章:
https://blog.csdn.net/Dongguabai/article/details/88736589

https://blog.csdn.net/K_520_W/article/details/121572228

查看命令:

  • jinfo -sysprops PID:可以查看由System.getProperties()取得的参数
  • jinfo -flags PID:查看曾经赋过值的参数
  • jinfo -flag 具体参数 PID:查看某个java进程的具体参数的值

修改命令:

  • 针对boolean类型:jinfo -flag [+ -]具体参数 PID
  • 针对非boolean类型:jinfo -flag 具体参数=具体参数值 PID

注意: 只有被标记为manageable的参数才可以被动态修改,查看被标记为manageable的参数

java -XX:+PrintFlagsFinal -version | grep manageable

4.3 拓展

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

5. jmap:导出内存映像文件&内存使用情况

5.1 基本情况

jmap(JVM Memory Map)

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

在这里插入图片描述

5.2 基本语法

基本语法:

  • jmap [option] < pid >
  • jmap [option] < 可执行的脚本>
  • jmap [option] [server_id@] < remote server IP or hostname>

其中option包括

选项作用
-dump生成java堆转储快照(dump文件),特别的:-dump:live 只保存堆中的存活对象
-finalizerinfo打印正等待回收的对象的信息,显示在F-Queue中等待Finalizer线程执行finalize方法的对象(仅在Linux/solaris平台有效
-heap输出整个堆空间的详细信息,包括GC的使用、堆配置信息、以及内存的使用信息等
-histo输出堆空间中对象的统计信息,包括类、实例数量和合计容量(特别的:-histo:live 只统计堆中存活的对象
-permstat以ClassLoader为统计口径输出永久代的内存状态信息(仅在Linux/solaris平台有效
-F当虚拟机进程对-dump选项内有任何响应时,强制执行生成dump文件(仅在Linux/solaris平台有效

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

5.3 使用一:导出内存映像文件

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

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

  • All Objects: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-frame information about local objects

说明:

  1. 通常在写Heap Dump文件之前会触发一次Full GC,所以Heap Dump文件里保存的都是Full GC后留下来的对象信息。
  2. 由于生成dump文件比较耗时,因此需要耐心等待,尤其是大内存镜像生成dump文件,则需要耗费更长的时间来完成。
5.3.1 手动方式导出
  • jmap -dump:format=b,file=< 文件名.hprof > < pid >

  • jmap -dump:live,format=b,file=< 文件名.hprof > < pid >

注意:

  1. format=b 作用是保证jmap生成的文件和hprof格式的文件保持一致。
  2. 实际使用-dump:live方式生成dump文件的场景较多。

测试代码

package jvm.classloader.jstat;

import java.util.ArrayList;

/**
 * jvm参数
 * -Xms60m -Xmx60m -XX:SurvivorRatio=8
 */
public class GCTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        for (int i = 0; i < 1000; i++) {
            byte[] arr = new byte[1024 * 100];//100kb
            list.add(arr);
            try {
                Thread.sleep(120);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.3.2 自动方式导出
  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:HeapDumpPath=< 文件名.hprof >

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

-XX:+HeapDumpOnOutOfMemoryError: 当程序发生OOM时,导出应用程序的当前堆快照。
-XX:HeapDumpPath=< 文件名.hprof >: 可以指定堆快照的保存位置。
比如:
-Xmx100m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/bldmac/ybl/m.hprof

测试代码

package jvm.classloader.jstat;

import java.util.ArrayList;

/**
 * jvm参数
 * -Xms60m -Xmx60m -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=/Users/bldmac/ybl/m.hprof
 */
public class GCTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        for (int i = 0; i < 1000; i++) {
            byte[] arr = new byte[1024 * 100];//100kb
            list.add(arr);
            try {
                Thread.sleep(120);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.4 使用二:显示堆内存相关信息

  • jmap -heap pid:运行这一刻,堆内存容量及使用情况。

  • jmap -histo pid:运行这一刻,堆中对象的相关信息。

测试代码和上述代码一样

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.5 使用三:其他作用

  • jmap -clstats pid:查看系统的ClassLoader信息
  • jmap -finalizerinfo:查看堆积在finalizer队列中的对象
    在这里插入图片描述
    在这里插入图片描述

5.6 小结

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

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

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

6. jhat:JDK自带堆分析工具(简单介绍)

6.1 基本情况

jhat(JVM Heap Analysis Tool)

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

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

说明:jhat命令在JDK9、JDK10中已经被删除,官方推荐使用VisualVM代替。

6.2 基本语法

帮助命令

在这里插入图片描述

jhat打开上述jmap生成的dump文件

在这里插入图片描述
端口被占用,可指定端口

jhat -port 6565 /Users/bldmac/ybl/1.hprof

在这里插入图片描述

打开浏览器

在这里插入图片描述
在这里插入图片描述

可以在浏览器中详细分析dump文件。

option参数

  • -stack false|true:关闭|打开对象分配调用栈跟踪。
  • -refs false|true:关闭|打开对象引用跟踪。
  • -port port-number:设置jhat http server的端口号,默认7000。
  • -exclude exclude-file:执行对象查询时需要排除的数据成员。
  • -baseline exclude-file:指定一个基准堆转储。
  • -debug int:设置debug级别。
  • -version:启动后显示版本信息就退出。
  • -J< flag >:传入启动参数,比如:-J -Xmx512m

7. jstack:打印JVM中线程快照

7.1 基本情况

jstack(JVM Stack Trace)

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

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

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

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

7.2 基本语法

帮助命令

在这里插入图片描述

基本语法为:jstack option pid

jstack管理远程进程的话,需要在远程程序的启动参数中增加:

-Djava.rmi.server.hostname=172.20.10.5
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

简单地说明以上的几个参数:

com.sun.management.jmxremote:开启 JMX 远程控制,在 JDK 1.6 之后该选项默认启用。
java.rmi.server.hostname:JMX 的监听地址。
com.sun.management.jmxremote.rmi.port:JMX 的监听端口。
com.sun.management.jmxremote.authenticate:是否开启鉴权。
com.sun.management.jmxremote.ssl:是否开启 SSL。

option参数

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

死锁测试代码

package jvm.classloader.jvm;

/**
 * 死锁测试
 */
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) {
                    throw new RuntimeException(e);
                }

                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) {
                    throw new RuntimeException(e);
                }

                synchronized (s1) {
                    s1.append("d");
                    s2.append("4");
                    System.out.println(s1);
                    System.out.println(s2);
                }
            }


        }).start();
    }
}

在这里插入图片描述
在这里插入图片描述
也可以导出到文件后查看。

在这里插入图片描述
在这里插入图片描述
ThreadSleep测试代码

package jvm.classloader.jvm;

public class ThreadSleepTest {
    public static void main(String[] args) {
        System.out.println("1111");
        try {
            Thread.sleep(1000000000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("2222");
    }
}

在这里插入图片描述
在这里插入图片描述

8. jcmd:多功能命令行

8.1 基本情况

在JDK1.7以后,新增了一个命令行工具jcmd。

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

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

8.2 基本语法

  • jcmd 与 jcmd -l:列出所有的JVM进程

在这里插入图片描述
在这里插入图片描述

  • jcmd -help:查看帮助文档

在这里插入图片描述

  • jcmd pid help:针对指定的进程,列出支持的所有命令

在这里插入图片描述

  • jcmd pid 具体命令:显示所有进程的指令命令的数据
  1. 类似jstack

在这里插入图片描述

  1. 类似jmap -histo pid


其他,可以自行测试

在这里插入图片描述

9. jstatd:远程主机信息收集(了解)

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

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

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM常用监控工具有很多,其中一个重要的工具就是dump分析工具。dump文件是指Java进程的内存快照,可以用于分析Java进程的内存使用情况,了解Java进程内部的情况。 下面介绍几个常用的dump分析工具: 1. jmap jmap是JDK自带的一个命令行工具,可以生成Java进程的内存快照。使用jmap生成dump文件的命令如下: ``` jmap -dump:format=b,file=<filename> <pid> ``` 其中,format=b表示生成进制格式的dump文件,file=<filename>表示指定保存dump文件的路径和文件名,<pid>表示Java进程的进程ID。 2. jstack jstack也是JDK自带的一个命令行工具,可以打印Java进程的线程堆栈信息。使用jstack生成dump文件的命令如下: ``` jstack -F <pid> > <filename> ``` 其中,-F表示在进程不响应时强制获取线程堆栈信息,<pid>表示Java进程的进程ID,> <filename>表示将线程堆栈信息输出到指定文件。 3. VisualVM VisualVM是一个功能强大的Java监控和分析工具,可以监控和分析本地和远程Java进程。VisualVM可以生成Java进程的各种信息,包括dump文件。使用VisualVM生成dump文件的步骤如下: - 在VisualVM中打开需要生成dump文件的Java进程。 - 选择“Heap Dump”选项卡,点击“Heap Dump”按钮。 - 选择保存dump文件的路径和文件名,点击“Save”按钮。 4. Eclipse Memory Analyzer Eclipse Memory Analyzer是一款功能强大的Java内存分析工具,可以帮助开发人员分析Java进程的内存使用情况。Eclipse Memory Analyzer可以打开各种格式的dump文件,包括使用jmap、jstack和VisualVM生成的dump文件。 以上是常用的dump分析工具,可以帮助开发人员了解Java进程的内存使用情况。同时,需要注意的是,生成dump文件会对Java进程产生一定的影响,需要谨慎使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值