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

概述

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

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

体会1:使用数据说明问题,使用知识分析问题,使用工具处理问题。

体会2:无监控、不调优!

简单命令行工具

源码位置:https://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/jdk.jcmd/share/classes/sun/tools

刚接触Java学习的时候,大家肯定最先了解的两个命令就是javac , java

那么 , 除此之外,还有没有其他的命令可以供我们使用呢?
我们进入到安装jdk中的bin目录,发现还有一系列辅助工具。这些辅助工具用来获取目标JVM不同方面、不同层次的信息,帮助开发人员很好地解决Java应用程序的一些疑难杂症。

查看java 版本:java -version

工具位置

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

jps:Java Process Status

基本情况

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

package com.atguigu.java;

import java.util.Scanner;

public class ScannerTest {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String info = scanner.next();
    }
}

可以看到有一一对应关系,jps每次都会变

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

基本语法

查看 jps 帮助

jps -help

options参数

jsp -q

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

jps -l

输出应用程序主类的全类名或如果进程执行的是jar包,则输出jar完整路径

jps -m

输出虚拟机进程启动时传递给主类main()的参数

可以看到atguigu参数

jps -v

列出虚拟机进程启动时的JVM参数

比如:-Xms20m -Xmx50m是启动程序指定的jvm参数

说明:以上参数可以综合使用。

补充:

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

hostid参数

RMI注册表中注册的主机名。

如果想要远程监控主机上的 java 程序,需要安装 jstatd。

对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易受到IP地址欺诈攻击。

如果安全问题无法使用一个定制的策略文件来处理,那么最安全的操作是不运行jstatd服务器,而是在本地使用jstat和jps工具。

jstat:查看JVM统计信息

基本情况

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

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

![2021-03-13 10-17-07屏幕截图](E:\我的坚果云\JVM监控及诊断工具(命令行篇).assets\2021-03-13 10-17-07屏幕截图.png)

官方文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html

基本语法

它的基本使用语法为:

Usage: jstat --help|-options
       jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

Definitions:
  <option>      An option reported by the -options option
  <vmid>        Virtual Machine Identifier. A vmid takes the following form:
                     <lvmid>[@<hostname>[:<port>]]
                Where <lvmid> is the local vm identifier for the target
                Java virtual machine, typically a process id; <hostname> is
                the name of the host running the target Java virtual machine;
                and <port> is the port number for the rmiregistry on the
                target host. See the jvmstat documentation for a more complete
                description of the Virtual Machine Identifier.
  <lines>       Number of samples between header lines.
  <interval>    Sampling interval. The following forms are allowed:
                    <n>["ms"|"s"]
                Where <n> is an integer and the suffix specifies the units as
                milliseconds("ms") or seconds("s"). The default units are "ms".
  <count>       Number of samples to take before terminating.
  -J<flag>      Pass <flag> directly to the runtime system.
  -? -h --help  Prints this help message.
  -help         Prints this help message.

option参数

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

类装载相关的:

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

垃圾回收相关的:

  • -gc:显示与GC相关的堆信息。包括Eden区、两个 Survivor区、老年代永久代等的容量、早用空间、GC时间合计等信息。

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

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

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

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

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

  • -gold:显示老年代GC状况

JIT 相关的:

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

类装载相关

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


JIT 相关

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


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


垃圾回收相关

package com.atguigu.java;

import java.util.ArrayList;

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

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

-gc:显示与GC相关的堆信息。包括Eden区、两个 Survivor区、老年代永久代等的容量、早用空间、GC时间合计等信息。

新生代相关
  • SOC 是第一个幸存者区的大小(字节)
  • S1C 是第二个幸存者区的大小(字节)
  • SOU 是第一个幸存者区已使用的大小(字节)
  • S1U 是第二个幸存者区已使用的大小(字节)
  • EC 是 Eden 空间的大小(字节)
  • EU 是 Eden 空间已使用大小(字节)
老年代相关
  • OC 是老年代的大小(字节)
  • OU 是老年代已使用的大小(字节)
方法区(元空间)相关
  • MC 是方法区的大小
  • MU 是方法区已使用的大小
  • CCSC 是压缩类空间的大小
  • CCSU 是压缩类空间已使用的大小
其它
  • YGC 是指从应用程序启动到采样时 young gc 次数
  • YGCT 是指从应用程序启动到采样时 young gc 消耗的时间(秒)
  • FGC 是指从应用程序启动到采样时 full gc 次数
  • FGCT 是指从应用程序启动到采样时 full gc 消耗的时间(秒)
  • GCT 是指从应用程序启动到采样时 gc 的总时间

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


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


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


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

interval参数

jstat -class PID s

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

count参数

jstat -class PID s n

用于指定查询的总次数

-t参数

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

jstat -class -t PID

经验

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

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

-h参数

jstat -class -hx PID

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

补充

jstat还可以用来判断是否出现内存泄漏 :

第 1 步:

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

第 2 步:

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

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

jinfo(Configuralion Info for Java):查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数。

在很多情况下,Java 应用程序不会指定所有的 Java 虚拟机参数。而此时,开发人员可能不知道某一个具体的 Java 虚拟机参数的默认值。

在这种情况下,可能需要通过查找文档获取某个参数的默认值。这个查找过程可能是非常艰难的。但有了 jinfo 工具,开发人员可以很方便地找到 Java 虚拟机参数的当前值。

基本语法

Usage:
    jinfo [option] <pid>
        (to connect to running process)
    jinfo [option] <executable <core>
        (to connect to a core file)
    jinfo [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties
    <no option>          to print both of the above
    -h | -help           to print this help message

jinfo [ options ] pid

[options]:

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

官方帮助文档:
JDK8:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jinfo.html

JDK11:https://docs.oracle.com/en/java/javase/11/tools/jinfo.html#GUID-69246B58-28C4-477D-B375-278F5F9830A5

查看

-sysprops:可以查看由 System.getProperties()取得的参数。

jinfo -sysprops PID


-flags:查看曾经赋过值的一些参数。


-flag:查看某个 java 进程具体参数。

修改

jinfo 不仅可以查看运行时某一个 Java 虚拟机参数的实际取值,甚至可以在运行时修改部分参数,并使之立即生效。

但是,并非所有参数都支持动态修改。参数只有被标记为 manageable 的 flag 可以被实时修改。其实,这个修改能力是极其有限的。

可以查看被标记为 manageable 的参数。

java -XX:+PrintFlagsFinal -version | grep manageable

针对 boolean 类型

jinfo -flag [+-]具体参数 PID

针对非 boolean 类型

jinfo -flag 具体参数=具体参数值 PID

扩展

java -XX+PrintFlagslnitial PID:查看所有 JVM 参数启动的初始值。

java -XX:+PrintFlagsFinal PID:查看所有 JVM 参数的最终值。

java -XX+PrintCommandLineFlags PID:查看已经被用户或者 JVM 设置过的详细参数名称和值。

jmap:导出内存映像文件/内存使用情况

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

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

官方帮助文档:

JDK8:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jmap.html

JDK11:https://docs.oracle.com/en/java/javase/11/tools/jmap.html#GUID-D2340719-82BA-4077-B0F3-2803269B7F41

基本语法

  • jmap [option] (to connect to running process)
  • jmap [option] <executable (to connect to a core file)
  • jmap [option] [server_id@] (to connect to remote debug server)

其中 option 包括:

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

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

生成 Java 堆转储快照文件:dump

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

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

All Object’s
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 文件则需要耗费更长的时间来完成。

手动的方式

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

jmap -dump:live,format=b,file=<filename.hprof> //存活对象

自动的方式

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

这里介绍一种比较常用的取得堆快照文件的方法,即使用:

-XX:+HeapDumpOnOutOfMemoryError:在程序发生 OOM 时,导出应用程序的当前堆快照

-XX:HeapDumpPath=<filename.hprof>:可以指定堆快照的保存位置

显示堆内存相关信息

查看各区大小:jmap -heap pid

所有类型使用的内存:jmap -histo pid

 num     #instances         #bytes  class name
----------------------------------------------
   1:           639       19396920  [B
   2:          4603         449512  [C
   3:          4455         106920  java.lang.String
   4:           691          78976  java.lang.Class
   5:           636          43344  [Ljava.lang.Object;
   6:           221          32408  [I
   7:           791          31640  java.util.TreeMap$Entry
   8:           644          25760  java.util.LinkedHashMap$Entry
   9:           426          18968  [Ljava.lang.String;
  10:           371          11872  java.util.HashMap$Node
  11:            23           8624  [Ljava.util.HashMap$Node;
  12:           113           8136  java.lang.reflect.Field
  13:           103           6592  java.net.URL
  14:           110           4400  java.lang.ref.SoftReference
  15:           256           4096  java.lang.Integer
  16:           121           3872  java.util.Hashtable$Entry
  17:           107           3424  java.util.concurrent.ConcurrentHashMap$Node
  18:            75           3000  java.lang.ref.Finalizer
  19:            50           2800  sun.misc.URLClassPath$JarLoader
  20:             6           2256  java.lang.Thread
  21:            26           2080  java.lang.reflect.Constructor
  22:            16           2048  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  23:            39           1872  sun.util.locale.LocaleObjectCache$CacheEntry
  24:            23           1840  [Ljava.util.WeakHashMap$Entry;
  25:             1           1568  [[B
  26:            31           1488  java.util.HashMap
  27:            37           1480  java.io.ObjectStreamField
  28:            26           1456  java.lang.Class$ReflectionData
  29:            15           1440  java.util.jar.JarFile$JarFileEntry
  30:            89           1424  java.lang.Object
  31:            57           1368  java.io.ExpiringCache$Entry
  32:            23           1288  sun.nio.cs.UTF_8$Encoder
  33:            20           1280  java.util.concurrent.ConcurrentHashMap
  34:            20           1280  java.util.jar.JarFile
  35:             9           1184  [Ljava.util.Hashtable$Entry;
  36:            20           1120  java.util.zip.ZipFile$ZipFileInputStream
  37:            23           1104  java.util.WeakHashMap
  38:             2           1064  [Ljava.lang.invoke.MethodHandle;
  39:             1           1040  [Ljava.lang.Integer;
  40:             1           1040  [[C
  41:            30            960  java.lang.ref.ReferenceQueue
  42:            19            760  sun.util.locale.BaseLocale$Key
  43:            18            720  sun.nio.cs.UTF_8$Decoder
  44:            11            648  [Ljava.lang.reflect.Field;
  45:             8            640  [S
  46:            20            640  java.util.zip.ZipCoder
  47:            19            608  java.io.File
  48:            19            608  java.util.Locale
  49:            19            608  sun.util.locale.BaseLocale
  50:            10            560  java.util.zip.ZipFile$ZipFileInflaterInputStream
  51:            32            512  java.lang.ref.ReferenceQueue$Lock
  52:            21            504  java.util.jar.Attributes$Name
  53:            20            480  java.util.ArrayDeque
  54:            10            480  java.util.zip.Inflater
  55:            19            456  java.util.Locale$LocaleKey
  56:            11            440  java.security.AccessControlContext
  57:            18            432  sun.misc.MetaIndex
  58:            10            400  java.io.FileDescriptor
  59:            13            392  [Ljava.io.ObjectStreamField;
  60:             1            384  com.intellij.rt.execution.application.AppMainV2$1
  61:             1            384  java.lang.ref.Finalizer$FinalizerThread
  62:             6            384  java.nio.DirectByteBuffer
  63:             1            376  java.lang.ref.Reference$ReferenceHandler
  64:             6            336  java.nio.DirectLongBufferU
  65:            17            328  [Ljava.lang.Class;
  66:            10            320  java.lang.OutOfMemoryError
  67:             3            312  [D
  68:            13            312  [Ljava.lang.reflect.Constructor;
  69:            13            312  sun.reflect.NativeConstructorAccessorImpl
  70:             2            296  [J
  71:            12            288  java.util.ArrayList
  72:             5            280  sun.util.calendar.ZoneInfo
  73:             3            264  java.lang.reflect.Method
  74:             8            256  java.util.Vector
  75:             3            240  [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;
  76:             5            240  java.util.Hashtable
  77:             6            240  java.util.WeakHashMap$Entry
  78:            10            240  java.util.zip.ZStreamRef
  79:             7            224  java.lang.ThreadLocal$ThreadLocalMap$Entry
  80:             4            224  java.util.LinkedHashMap
  81:             9            216  java.util.LinkedList$Node
  82:            13            208  sun.reflect.DelegatingConstructorAccessorImpl
  83:             5            200  java.security.ProtectionDomain
  84:             6            192  java.io.FileInputStream
  85:             4            192  java.util.Properties
  86:             4            192  java.util.TreeMap
  87:             3            168  sun.nio.cs.ext.DoubleByte$Decoder
  88:             2            160  [[Ljava.lang.String;
  89:             4            160  java.lang.ClassLoader$NativeLibrary
  90:             5            160  java.security.CodeSource
  91:             5            160  java.util.LinkedList
  92:             5            160  sun.util.locale.provider.LocaleProviderAdapter$Type
  93:             3            144  java.nio.HeapByteBuffer
  94:             6            144  sun.misc.PerfCounter
  95:             3            144  sun.misc.URLClassPath
  96:             2            128  java.io.ExpiringCache$1
  97:             4            128  java.util.Stack
  98:             2            128  sun.nio.cs.ext.DoubleByte$Encoder
  99:             1            120  java.net.SocksSocketImpl
 100:             5            120  java.util.Collections$UnmodifiableRandomAccessList
 101:             5            120  sun.misc.FloatingDecimal$PreparedASCIIToBinaryBuffer
 102:             2            112  java.lang.Package
 103:             2            112  java.util.ResourceBundle$CacheKey
 104:             3             96  java.io.FileOutputStream
 105:             4             96  java.lang.RuntimePermission
 106:             3             96  java.lang.StringCoding$StringEncoder
 107:             2             96  java.lang.ThreadGroup
 108:             2             96  java.util.ResourceBundle$BundleReference
 109:             1             96  sun.misc.Launcher$AppClassLoader
 110:             3             96  sun.net.spi.DefaultProxySelector$NonProxyInfo
 111:             2             96  sun.nio.cs.StreamEncoder
 112:             1             88  java.net.DualStackPlainSocketImpl
 113:             1             88  sun.misc.Launcher$ExtClassLoader
 114:             5             80  [Ljava.security.Principal;
 115:             2             80  java.io.BufferedWriter
 116:             2             80  java.io.ExpiringCache
 117:             5             80  java.lang.ThreadLocal
 118:             5             80  java.security.ProtectionDomain$Key
 119:             2             80  sun.misc.FloatingDecimal$BinaryToASCIIBuffer
 120:             3             72  java.lang.ThreadLocal$ThreadLocalMap
 121:             3             72  java.net.Proxy$Type
 122:             3             72  java.util.Arrays$ArrayList
 123:             3             72  java.util.Collections$SynchronizedSet
 124:             1             72  java.util.ResourceBundle$RBClassLoader
 125:             3             72  java.util.concurrent.atomic.AtomicLong
 126:             3             72  sun.misc.FloatingDecimal$ExceptionalBinaryToASCIIBuffer
 127:             1             72  sun.util.locale.provider.JRELocaleProviderAdapter
 128:             1             64  [F
 129:             2             64  [Ljava.lang.Thread;
 130:             2             64  java.io.FilePermission
 131:             2             64  java.io.PrintStream
 132:             2             64  java.lang.ClassValue$Entry
 133:             2             64  java.lang.StringCoding$StringDecoder
 134:             2             64  java.lang.VirtualMachineError
 135:             2             64  java.lang.ref.ReferenceQueue$Null
 136:             2             64  java.lang.ref.WeakReference
 137:             2             64  java.security.BasicPermissionCollection
 138:             2             64  java.security.Permissions
 139:             4             64  java.util.HashSet
 140:             2             64  java.util.ResourceBundle$LoaderReference
 141:             2             48  [Ljava.lang.reflect.Method;
 142:             2             48  java.io.BufferedOutputStream
 143:             1             48  java.io.BufferedReader
 144:             2             48  java.io.File$PathStatus
 145:             2             48  java.io.FilePermissionCollection
 146:             2             48  java.io.OutputStreamWriter
 147:             2             48  java.net.InetAddress$Cache
 148:             2             48  java.net.InetAddress$Cache$Type
 149:             1             48  java.net.SocketInputStream
 150:             1             48  java.nio.HeapCharBuffer
 151:             2             48  java.nio.charset.CoderResult
 152:             3             48  java.nio.charset.CodingErrorAction
 153:             2             48  sun.misc.NativeSignalHandler
 154:             2             48  sun.misc.Signal
 155:             3             48  sun.net.www.protocol.jar.Handler
 156:             1             48  sun.nio.cs.StreamDecoder
 157:             1             48  sun.nio.cs.US_ASCII$Decoder
 158:             1             48  sun.util.locale.provider.LocaleResources$ResourceReference
 159:             1             48  sun.util.resources.TimeZoneNames
 160:             1             48  sun.util.resources.en.TimeZoneNames_en
 161:             1             40  [Lsun.util.locale.provider.LocaleProviderAdapter$Type;
 162:             1             40  java.io.BufferedInputStream
 163:             1             40  java.util.ResourceBundle$1
 164:             1             40  sun.nio.cs.StandardCharsets$Aliases
 165:             1             40  sun.nio.cs.StandardCharsets$Cache
 166:             1             40  sun.nio.cs.StandardCharsets$Classes
 167:             1             40  sun.nio.cs.ext.ExtendedCharsets
 168:             1             32  [Ljava.lang.OutOfMemoryError;
 169:             2             32  [Ljava.lang.StackTraceElement;
 170:             1             32  [Ljava.lang.ThreadGroup;
 171:             1             32  [Ljava.net.Proxy$Type;
 172:             1             32  java.io.WinNTFileSystem
 173:             1             32  java.lang.ArithmeticException
 174:             2             32  java.lang.Boolean
 175:             1             32  java.lang.NullPointerException
 176:             1             32  java.net.InetAddress$InetAddressHolder
 177:             1             32  java.net.Socket
 178:             2             32  java.nio.ByteOrder
 179:             2             32  java.util.LinkedHashMap$LinkedKeySet
 180:             2             32  java.util.concurrent.atomic.AtomicInteger
 181:             1             32  java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl
 182:             1             32  sun.instrument.InstrumentationImpl
 183:             1             32  sun.nio.cs.StandardCharsets
 184:             1             32  sun.util.locale.provider.LocaleResources
 185:             1             32  sun.util.locale.provider.LocaleServiceProviderPool
 186:             1             24  [Ljava.io.File$PathStatus;
 187:             1             24  [Ljava.lang.ClassValue$Entry;
 188:             1             24  [Ljava.net.InetAddress$Cache$Type;
 189:             1             24  [Ljava.security.ProtectionDomain;
 190:             1             24  [Lsun.launcher.LauncherHelper;
 191:             1             24  java.io.InputStreamReader
 192:             1             24  java.lang.ClassValue$Version
 193:             1             24  java.lang.StringBuilder
 194:             1             24  java.lang.invoke.MethodHandleImpl$4
 195:             1             24  java.lang.reflect.ReflectPermission
 196:             1             24  java.net.Inet4Address
 197:             1             24  java.net.Inet6AddressImpl
 198:             1             24  java.net.Proxy
 199:             1             24  java.util.BitSet
 200:             1             24  java.util.Collections$EmptyMap
 201:             1             24  java.util.Collections$SetFromMap
 202:             1             24  java.util.Locale$Cache
 203:             1             24  java.util.ResourceBundle$Control$CandidateListCache
 204:             1             24  java.util.jar.Manifest
 205:             1             24  sun.instrument.TransformerManager
 206:             1             24  sun.launcher.LauncherHelper
 207:             1             24  sun.misc.JarIndex
 208:             1             24  sun.misc.URLClassPath$FileLoader
 209:             1             24  sun.nio.cs.ISO_8859_1
 210:             1             24  sun.nio.cs.ThreadLocalCoders$1
 211:             1             24  sun.nio.cs.ThreadLocalCoders$2
 212:             1             24  sun.nio.cs.US_ASCII
 213:             1             24  sun.nio.cs.UTF_16
 214:             1             24  sun.nio.cs.UTF_16BE
 215:             1             24  sun.nio.cs.UTF_16LE
 216:             1             24  sun.nio.cs.UTF_8
 217:             1             24  sun.nio.cs.ext.GBK
 218:             1             24  sun.reflect.NativeMethodAccessorImpl
 219:             1             24  sun.util.locale.BaseLocale$Cache
 220:             1             24  sun.util.locale.provider.TimeZoneNameProviderImpl
 221:             1             16  [Ljava.lang.Throwable;
 222:             1             16  [Ljava.security.cert.Certificate;
 223:             1             16  [Lsun.instrument.TransformerManager$TransformerInfo;
 224:             1             16  java.io.FileDescriptor$1
 225:             1             16  java.lang.CharacterDataLatin1
 226:             1             16  java.lang.ClassValue$Identity
 227:             1             16  java.lang.Runtime
 228:             1             16  java.lang.String$CaseInsensitiveComparator
 229:             1             16  java.lang.System$2
 230:             1             16  java.lang.Terminator$1
 231:             1             16  java.lang.invoke.MemberName$Factory
 232:             1             16  java.lang.invoke.MethodHandleImpl$2
 233:             1             16  java.lang.invoke.MethodHandleImpl$3
 234:             1             16  java.lang.ref.Reference$1
 235:             1             16  java.lang.ref.Reference$Lock
 236:             1             16  java.lang.reflect.ReflectAccess
 237:             1             16  java.net.InetAddress$2
 238:             1             16  java.net.URLClassLoader$7
 239:             1             16  java.nio.Bits$1
 240:             1             16  java.nio.charset.CoderResult$1
 241:             1             16  java.nio.charset.CoderResult$2
 242:             1             16  java.security.ProtectionDomain$2
 243:             1             16  java.security.ProtectionDomain$JavaSecurityAccessImpl
 244:             1             16  java.util.Collections$EmptyIterator
 245:             1             16  java.util.Collections$EmptyList
 246:             1             16  java.util.Collections$EmptySet
 247:             1             16  java.util.Hashtable$EntrySet
 248:             1             16  java.util.ResourceBundle$Control
 249:             1             16  java.util.WeakHashMap$KeySet
 250:             1             16  java.util.concurrent.atomic.AtomicBoolean
 251:             1             16  java.util.jar.Attributes
 252:             1             16  java.util.jar.JavaUtilJarAccessImpl
 253:             1             16  java.util.zip.ZipFile$1
 254:             1             16  sun.misc.ASCIICaseInsensitiveComparator
 255:             1             16  sun.misc.FloatingDecimal$1
 256:             1             16  sun.misc.Launcher
 257:             1             16  sun.misc.Launcher$Factory
 258:             1             16  sun.misc.Perf
 259:             1             16  sun.misc.Unsafe
 260:             1             16  sun.net.spi.DefaultProxySelector
 261:             1             16  sun.net.www.protocol.file.Handler
 262:             1             16  sun.reflect.DelegatingMethodAccessorImpl
 263:             1             16  sun.reflect.ReflectionFactory
 264:             1             16  sun.util.calendar.Gregorian
 265:             1             16  sun.util.locale.provider.AuxLocaleProviderAdapter$NullProvider
 266:             1             16  sun.util.locale.provider.SPILocaleProviderAdapter
 267:             1             16  sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter
 268:             1             16  sun.util.resources.LocaleData
 269:             1             16  sun.util.resources.LocaleData$LocaleDataResourceBundleControl
Total         15721       20298912

由于 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 代替。

堆直方图

OQL查询

option 参数

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

jstack:打印 JVM 中线程快照

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

生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。

这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用 jstack 显示各个线程调用的堆栈情况。

官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jstack.html#GUID-721096FC-237B-473C-A461-DBBBB79E4F6A

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

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

基本语法

jstack [option] pid

package com.atguigu.java;

public class DeadLock {
    private static Object firstMonitor = new Object();
    private static Object secondMonitor = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                synchronized (firstMonitor) {
                    synchronized (secondMonitor) {
                        System.out.println("Thread1");
                    }
                }
            }
        }).start();

        new Thread(() -> {
            while (true) {
                synchronized (secondMonitor) {
                    synchronized (firstMonitor) {
                        System.out.println("Thread2");
                    }
                }
            }
        }).start();
    }
}

分析出死锁问题

2021-03-13 12:25:35
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.202-b08 mixed mode):

"DestroyJavaVM" #12 prio=5 os_prio=0 tid=0x0000000001d20000 nid=0x2848 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #11 prio=5 os_prio=0 tid=0x0000000058882000 nid=0x1bb8 waiting for monitor entry [0x000000005933e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.atguigu.java.DeadLock.lambda$main$1(DeadLock.java:21)
	- waiting to lock <0x00000000d5a6f208> (a java.lang.Object)
	- locked <0x00000000d5a6f218> (a java.lang.Object)
	at com.atguigu.java.DeadLock$$Lambda$2/764977973.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)

"Thread-0" #10 prio=5 os_prio=0 tid=0x0000000058881800 nid=0x22c4 waiting for monitor entry [0x000000005920e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.atguigu.java.DeadLock.lambda$main$0(DeadLock.java:12)
	- waiting to lock <0x00000000d5a6f218> (a java.lang.Object)
	- locked <0x00000000d5a6f208> (a java.lang.Object)
	at com.atguigu.java.DeadLock$$Lambda$1/1452126962.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)

"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00000000585ce800 nid=0x2b4c runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000000058594000 nid=0x26d0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000058569800 nid=0x1f50 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000058566000 nid=0x272c runnable [0x0000000058a6e000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
	at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
	at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
	- locked <0x00000000d5b3d310> (a java.io.InputStreamReader)
	at java.io.InputStreamReader.read(InputStreamReader.java:184)
	at java.io.BufferedReader.fill(BufferedReader.java:161)
	at java.io.BufferedReader.readLine(BufferedReader.java:324)
	- locked <0x00000000d5b3d310> (a java.io.InputStreamReader)
	at java.io.BufferedReader.readLine(BufferedReader.java:389)
	at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:47)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000005751b800 nid=0x1fb0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000057509800 nid=0x1494 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x00000000574e0800 nid=0x1e1c in Object.wait() [0x000000005835f000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000d5908ed0> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
	- locked <0x00000000d5908ed0> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x00000000574d7800 nid=0x284c in Object.wait() [0x000000005820e000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x00000000d5906bf8> (a java.lang.ref.Reference$Lock)
	at java.lang.Object.wait(Object.java:502)
	at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
	- locked <0x00000000d5906bf8> (a java.lang.ref.Reference$Lock)
	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x000000005748f800 nid=0x1c24 runnable 

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000001d36800 nid=0x2ba8 runnable 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000001d38000 nid=0x1d94 runnable 

"VM Periodic Task Thread" os_prio=2 tid=0x0000000058631000 nid=0x283c waiting on condition 

JNI global references: 317


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00000000574dd8e8 (object 0x00000000d5a6f208, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00000000574dd998 (object 0x00000000d5a6f218, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at com.atguigu.java.DeadLock.lambda$main$1(DeadLock.java:21)
	- waiting to lock <0x00000000d5a6f208> (a java.lang.Object)
	- locked <0x00000000d5a6f218> (a java.lang.Object)
	at com.atguigu.java.DeadLock$$Lambda$2/764977973.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)
"Thread-0":
	at com.atguigu.java.DeadLock.lambda$main$0(DeadLock.java:12)
	- waiting to lock <0x00000000d5a6f218> (a java.lang.Object)
	- locked <0x00000000d5a6f208> (a java.lang.Object)
	at com.atguigu.java.DeadLock$$Lambda$1/1452126962.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

package com.atguigu.java;

/**
 * 演示线程:TIMED_WAITING
 */
public class ThreadSleepTest {
    public static void main(String[] args) {
        System.out.println("hello - 1");
        try {
            Thread.sleep(1000 * 60 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


package com.atguigu.java;

/**
 * 演示同步
 */
public class ThreadSyncTest {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);

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

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

class Number extends Thread {
    private int number = 1;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (number < 100) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                }
            }
        }
    }
}

option 参数

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

显示锁的附加信息

补充

使用java代码打印所有线程的状态

package com.atguigu.java;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;

public class AllStackTrace {
    public static void main(String[] args) {
        Map<Thread, StackTraceElement[]> all = Thread.getAllStackTraces();
        Set<Map.Entry<Thread, StackTraceElement[]>> entries = all.entrySet();
        for (Map.Entry<Thread, StackTraceElement[]> entry : entries) {
            Thread t = entry.getKey();
            StackTraceElement[] v = entry.getValue();
            System.out.println("[Thread name is \"" + t.getName() + "\"]");
            System.out.println(Arrays.deepToString(v));
        }
    }
}
[Thread name is "Signal Dispatcher"]
[]
[Thread name is "Finalizer"]
[java.lang.Object.wait(Native Method), java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144), java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165), java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)]
[Thread name is "Monitor Ctrl-Break"]
[java.net.SocketInputStream.socketRead0(Native Method), java.net.SocketInputStream.socketRead(SocketInputStream.java:116), java.net.SocketInputStream.read(SocketInputStream.java:171), java.net.SocketInputStream.read(SocketInputStream.java:141), sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284), sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326), sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178), java.io.InputStreamReader.read(InputStreamReader.java:184), java.io.BufferedReader.fill(BufferedReader.java:161), java.io.BufferedReader.readLine(BufferedReader.java:324), java.io.BufferedReader.readLine(BufferedReader.java:389), com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:47)]
[Thread name is "Attach Listener"]
[]
[Thread name is "Reference Handler"]
[java.lang.Object.wait(Native Method), java.lang.Object.wait(Object.java:502), java.lang.ref.Reference.tryHandlePending(Reference.java:191), java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)]
[Thread name is "main"]
[java.lang.Thread.dumpThreads(Native Method), java.lang.Thread.getAllStackTraces(Thread.java:1610), com.atguigu.java.AllStackTrace.main(AllStackTrace.java:9)]

Process finished with exit code 0

jcmd:多功能命令行

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

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

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

基本语法

jcmd -help

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


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


jcmd pid 具体命令:显示指定进程的指令命令数据。

检查死锁

堆直方图

dump文件

jstatd:远程主机信息收集

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

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

posted @ 2021-03-13 13:07  我係死肥宅  阅读(490)  评论(0编辑  收藏  举报