目录

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

# 概述

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

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

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

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

简单命令行工具

在我们刚接触 java 学习的时候,大家肯定最先了解的两个命令就是 javacjava,那么除此之外,还有没有其他的命令可以供我们使用呢?

我们进入到安装 jdk 的 bin 目录,发现还有一系列辅助工具。这些辅助工具用来获取目标 JVM 不同方面、不同层次的信息,帮助开发人员很好地解决 Java 应用程序的一些疑难杂症。

image-20220131152127843

官方源码地址:http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/jdk.jcmd/share/classes/sun/tools

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

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

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

基本使用语法为:jps [options] [hostid]

代码示例

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

运行后,在命令行输入 jps 查看进程:

image-20220131153051380

我们还可以通过追加参数,来打印额外的信息。

options 参数

  • -q:仅仅显示 LVMID(local virtual machine id),即本地虚拟机唯一 id。不显示主类的名称等
  • -l:输出应用程序主类的全类名或如果进程执行的是 jar 包,则输出 jar 完整路径
  • -m:输出虚拟机进程启动时传递给主类 main() 的参数
  • -v:列出虚拟机进程启动时的 JVM 参数。比如:-Xms20m -Xmx50m 是启动程序指定的 jvm 参数

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

如图:

image-20220131153248445

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

hostid 参数

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

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

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

# jstat:查看 JVM 统计信息

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

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

基本使用语法为:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

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

其中 vmid 是进程 id 号,也就是 jps 之后看到的前面的号码,如下:

image-20220131153629853

# option参数

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

类装载相关的

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

垃圾回收相关的

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

JIT 相关的

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

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

jstat -class

image-20220131153919947

jstat -compiler

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

image-20220131154943898

jstat -printcompilation

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

image-20220131155122971

jstat -gc

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

执行代码:

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 (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

JVM 参数:

-Xms60m -Xmx60m -XX:SurvivorRatio=8
1

运行后利用命令查询:

image-20220131160826978

表头 含义(字节)
S0C 幸存者 0 区的大小
S1C 幸存者 1 区的大小
S0U 幸存者 0 区已使用的大小
S1U 幸存者 1 区已使用的大小
EC Eden 区的大小
EU Eden 区已使用的大小
OC 老年代的大小
OU 老年代已使用的大小
MC 元空间的大小
MU 元空间已使用的大小
CCSC 压缩类空间的大小
CCSU 压缩类空间已使用的大小
YGC 从应用程序启动到采样时 Young GC 的次数
YGCT 从应用程序启动到采样时 Young GC 消耗时间(秒)
FGC 从应用程序启动到采样时 Full GC 的次数
FGCT 从应用程序启动到采样时的 Full GC 的消耗时间(秒)
GCT 从应用程序启动到采样时 GC 的总时间

后面的参数代表 1000 毫秒打印一次,一个打印 10 次。

jstat -gccapacity

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

image-20220131160931520

jstat -gcutil

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

image-20220131161054138

表头 含义(字节)
SO Survivor 0 区空间百分比
S1 Survivor 1 区空间百分比
E Eden 区空间百分比
O Old 区空间百分比
N 方法区空间百分比
CCS 压缩空间百分比
YGC 从应用程序启动到采样时 Young GC 的次数
YGCT 从应用程序启动到采样时 Young GC 消耗时间(秒)
FGC 从应用程序启动到采样时 Full GC 的次数
FGCT 从应用程序启动到采样时的 Full GC 的消耗时间(秒)
GCT 从应用程序启动到采样时 GC 的总时间

jstat -gccause

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

image-20220131161322060

jstat -gcnew

显示新生代 GC 状况。

image-20220131161525208

jstat -gcnewcapacity

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

image-20220131161618811

jstat -gcold

显示老年代 GC 状况。

image-20220131161659710

jstat -gcoldcapacity

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

image-20220131161741659

# 其他参数

下面的参数都是配合 option 参数后面使用。

基本使用语法为:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

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

  • count 参数:用于指定查询的总次数

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

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

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

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

image-20220131154811037

jstat -t

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

image-20220131161938786

jstat -t -h

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

image-20220131162141993

# 其他功能

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

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

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

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

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

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

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

基本使用语法为:jinfo [options] pid

说明:java 进程 ID 必须要加上。

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

# 参数查看

jinfo -sysprops

> jinfo -sysprops
jboss.modules.system.pkgs = com.intellij.rt
java.vendor = Oracle Corporation
sun.java.launcher = SUN_STANDARD
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
catalina.useNaming = true
os.name = Windows 10
...
1
2
3
4
5
6
7
8

jinfo -flags

C:\Users\24560>jps
13968 GCTest
22016 Launcher
8912
17740 Jps

C:\Users\24560>jinfo -flags 13968

# 输出结果如下:
Attaching to process ID 13968, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.231-b11
Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=62914560 -XX:MaxHeapSize=62914560 -XX:MaxNewSize=20971520 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=20971520 -XX:OldSize=41943040 -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Command line:  -Xms60m -Xmx60m -XX:SurvivorRatio=8 -javaagent:F:\Programming area\Ideal\IntelliJ IDEA 2020.3\lib\idea_rt.jar=55919:F:\Programming area\Ideal\IntelliJ IDEA 2020.3\bin -Dfile.encoding=UTF-8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

jinfo -flag name

查看当前的 Java 程序是否使用了指定的参数。

> jinfo -flag UseParallelGC 13968
-XX:+UseParallelGC

> jinfo -flag UseG1GC 13968
-XX:-UseG1GC
1
2
3
4
5

# 参数修改

jinfo -flag [+-]name

开启或者关闭对应名称的参数 只有被标记为 manageable 的参数才可以被动态修改。

> jinfo -flag +PrintGCDetails 13968
> jinfo -flag PrintGCDetails 13968
-XX:+PrintGCDetails

> jinfo -flag -PrintGCDetails 13968
> jinfo -flag PrintGCDetails 13968
-XX:-PrintGCDetails
1
2
3
4
5
6
7

# 参数拓展

java -XX:+PrintFlagsInitial

查看所有 JVM 参数启动的初始值。

[Global flags]
     intx ActiveProcessorCount                      = -1                                  {product}
    uintx AdaptiveSizeDecrementScaleFactor          = 4                                   {product}
    uintx AdaptiveSizeMajorGCDecayTimeScale         = 10                                  {product}
    uintx AdaptiveSizePausePolicy                   = 0                                   {product}
...
1
2
3
4
5
6

java -XX:+PrintFlagsFinal

查看所有 JVM 参数的最终值。

java -XX:+PrintFlagsFinal -version | grep manageable:可以查看被标记为 manageable 的参数。

manageable 参数就是在 Java 程序启动后还能重新设置的参数,如 GC 就不能在启动程序后再重新修改,所以不是 manageable 参数。

[Global flags]
     intx ActiveProcessorCount                      = -1                                  {product}
...
     intx CICompilerCount                          := 4                                   {product}
    uintx InitialHeapSize                          := 333447168                           {product}
    uintx MaxHeapSize                              := 1029701632                          {product}
    uintx MaxNewSize                               := 1774714880                          {product}
1
2
3
4
5
6
7

java -XX:+PrintCommandLineFlags

查看哪些已经被用户或者 JVM 设置过的详细的 XX 参数的名称和值。

-XX:InitialHeapSize=332790016 -XX:MaxHeapSize=5324640256 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
1

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

官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jmap.html

jmap(JVM Memory Map):作用一方面是获取 dump 文件(堆转储快照文件,二进制文件),它还可以获取目标 Java 进程的内存相关信息,包括 Java 堆各区域的使用情况、堆中对象的统计信息、类加载信息等。开发人员可以在控制台中输入命令 jmap -help 查阅 jmap 工具的具体使用方式和一些标准选项配置。

# 基本语法

基本使用语法为:

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

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

# 导出映像文件

手动方式

手动生成 dump 文件。相关的两个命令如下:

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

使用如图:

image-20220131172515141

在 D 盘生成了 4 个文件,其中 4.hprof 用了第二个命令生成。

image-20220131173748601

文件的大小依次递增,因为运行越久,里面的对象越多,占用的空间越大。

建议使用 jmap -dump:live,format=b,file=<filename.hprof> <pid> 命令生成 dump 文件,因为只需要获取存活的对象,没必要把被回收或者死亡的对象 dump 到文件里,导致文件太大。

自动方式

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

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

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

-XX:HeapDumpPath=<filename.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(60);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

设置 JVM 参数:

-Xms60m -Xmx60m -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=D:\5.hprof
1
2

运行代码后,当发生 OOM 异常,则立马在 D 盘生成 5.hprof 文件。

image-20220131173702078

# 显示堆内存相关信息

两个常用命令:

  • jmap -heap pid
  • jmap -histo pid

命令行示例:

jps
17744 Launcher
1904 GCTest
8912
15316 Jps

jmap -heap 3540 > a.txt

jmap -histo 1904 > b.txt
1
2
3
4
5
6
7
8
9

image-20220131211705430

两个命令将信息分别写到了 a.txt、b.txt 文件。

a.txt 内容为:

Attaching to process ID 1904, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.231-b11

using thread-local object allocation.
Parallel GC with 10 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4255121408 (4058.0MB)
   NewSize                  = 88604672 (84.5MB)
   MaxNewSize               = 1418199040 (1352.5MB)
   OldSize                  = 177733632 (169.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 66584576 (63.5MB)
   used     = 31136256 (29.69384765625MB)
   free     = 35448320 (33.80615234375MB)
   46.761964812992126% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation
   capacity = 177733632 (169.5MB)
   used     = 0 (0.0MB)
   free     = 177733632 (169.5MB)
   0.0% used

3163 interned Strings occupying 259640 bytes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

b.txt 内容:


 num     #instances         #bytes  class name
----------------------------------------------
   1:          1610       44293784  [B
   2:           659        2906760  [I
   3:          7774         906192  [C
   4:          5933         142392  java.lang.String
   5:           691          79112  java.lang.Class
   6:          1305          67680  [Ljava.lang.Object;
   7:           791          31640  java.util.TreeMap$Entry
   8:           628          25120  java.util.LinkedHashMap$Entry
   9:           456          22168  [Ljava.lang.String;
  10:           364          11648  java.util.HashMap$Node
  11:            37          11504  [Ljava.util.HashMap$Node;
  12:           152          10944  java.lang.reflect.Field
  13:           438          10512  java.lang.StringBuilder
  14:           242           7744  java.util.Hashtable$Entry
  15:           233           7456  java.io.File
  16:           101           6464  java.net.URL
  17:           241           5784  java.lang.StringBuffer
  18:           125           5000  java.lang.ref.SoftReference
  19:           258           4128  java.lang.Integer
  20:           117           3744  java.util.concurrent.ConcurrentHashMap$Node
  21:            25           3648  [Ljava.util.Hashtable$Entry;
  22:            75           3600  java.nio.HeapCharBuffer
  23:            74           3552  java.nio.HeapByteBuffer
  24:            42           3360  [S
  25:             8           3008  java.lang.Thread
  26:             5           2568  [J
  27:            20           2496  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  28:            44           2464  sun.misc.URLClassPath$JarLoader
  29:             2           2384  [[Ljava.lang.Object;
  30:            89           2136  java.net.Parts
  31:             2           2080  [[C
  32:            26           2080  java.lang.reflect.Constructor
  33:            39           1872  sun.util.locale.LocaleObjectCache$CacheEntry
  34:            43           1720  java.lang.ref.Finalizer
  35:            34           1632  java.util.HashMap
  36:             1           1568  [[B
  37:            37           1480  java.io.ObjectStreamField
  38:            26           1456  java.lang.Class$ReflectionData
  39:            91           1456  java.lang.Object
  40:            20           1280  java.util.concurrent.ConcurrentHashMap
  41:            15           1200  [Ljava.util.WeakHashMap$Entry;
  42:            45           1080  sun.misc.URLClassPath$3
  43:             2           1064  [Ljava.lang.invoke.MethodHandle;
  44:            11           1056  java.util.jar.JarFile$JarFileEntry
  45:             1           1040  [Ljava.lang.Integer;
  46:            41            984  java.io.ExpiringCache$Entry
  47:            11            880  java.util.zip.ZipEntry
  48:            22            880  sun.util.locale.BaseLocale$Key
  49:            47            840  [Ljava.lang.Class;
  50:            15            840  sun.nio.cs.UTF_8$Encoder
  51:            34            816  sun.security.action.GetPropertyAction
  52:            12            768  java.util.jar.JarFile
  53:            12            752  [Ljava.lang.reflect.Field;
  54:            15            720  java.util.WeakHashMap
  55:            22            704  java.lang.ref.ReferenceQueue
  56:            17            680  java.util.WeakHashMap$Entry
  57:            12            672  java.util.zip.ZipFile$ZipFileInputStream
  58:            19            608  java.util.Locale
  59:            19            608  sun.util.locale.BaseLocale
  60:            14            560  sun.nio.cs.UTF_8$Decoder
  61:            22            528  java.util.Locale$LocaleKey
  62:            13            520  java.security.AccessControlContext
  63:            21            504  java.util.jar.Attributes$Name
  64:            18            432  sun.misc.MetaIndex
  65:            10            400  java.io.FileDescriptor
  66:            13            392  [Ljava.io.ObjectStreamField;
  67:             1            384  com.intellij.rt.execution.application.AppMainV2$1
  68:             1            384  java.lang.ref.Finalizer$FinalizerThread
  69:            24            384  java.lang.ref.ReferenceQueue$Lock
  70:             6            384  java.nio.DirectByteBuffer
  71:            16            384  java.util.ArrayList
  72:            12            384  java.util.zip.ZipCoder
  73:             1            376  java.lang.ref.Reference$ReferenceHandler
  74:             4            352  java.lang.reflect.Method
  75:             6            336  java.nio.DirectLongBufferU
  76:             7            336  java.util.Hashtable
  77:             7            336  java.util.Hashtable$Enumerator
  78:             6            336  java.util.LinkedHashMap
  79:            14            336  sun.misc.FileURLMapper
  80:             6            336  sun.nio.cs.ext.DoubleByte$Decoder
  81:            10            320  java.lang.OutOfMemoryError
  82:            10            320  java.util.Vector
  83:             3            312  [D
  84:            13            312  [Ljava.lang.reflect.Constructor;
  85:            13            312  java.lang.Class$1
  86:            13            312  sun.reflect.NativeConstructorAccessorImpl
  87:            12            288  java.util.ArrayDeque
  88:             6            288  java.util.Properties
  89:             3            280  [[Ljava.lang.String;
  90:             5            280  java.util.ResourceBundle$CacheKey
  91:             5            280  sun.util.calendar.ZoneInfo
  92:             3            240  [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;
  93:             3            232  [Ljava.net.URL;
  94:             7            224  java.lang.ThreadLocal$ThreadLocalMap$Entry
  95:             7            224  java.security.CodeSource
  96:            14            224  sun.misc.URLClassPath$JarLoader$1
  97:             9            216  java.util.LinkedList$Node
  98:             2            208  sun.net.www.protocol.file.FileURLConnection
  99:            13            208  sun.reflect.DelegatingConstructorAccessorImpl
 100:             5            200  java.security.ProtectionDomain
 101:             3            192  [Ljava.io.File;
 102:             4            192  java.io.BufferedReader
 103:             6            192  java.io.FileInputStream
 104:             8            192  java.net.URLClassLoader$1
 105:             4            192  java.util.TreeMap
 106:             4            192  sun.nio.cs.StreamDecoder
 107:             4            192  sun.nio.cs.StreamEncoder
 108:             3            192  sun.nio.cs.ext.DoubleByte$Encoder
 109:             2            192  sun.util.calendar.Gregorian$Date
 110:             4            160  java.io.BufferedWriter
 111:             4            160  java.lang.ClassLoader$NativeLibrary
 112:             4            160  java.lang.ClassNotFoundException
 113:             4            160  java.security.PrivilegedActionException
 114:             5            160  java.util.LinkedList
 115:             5            160  java.util.ResourceBundle$LoaderReference
 116:             5            160  sun.util.locale.provider.LocaleProviderAdapter$Type
 117:             3            144  java.util.StringTokenizer
 118:             6            144  sun.misc.PerfCounter
 119:             3            144  sun.misc.URLClassPath
 120:             6            144  sun.usagetracker.UsageTrackerClient$1
 121:             2            128  java.io.ExpiringCache$1
 122:             4            128  java.lang.StringCoding$StringDecoder
 123:             4            128  java.lang.StringCoding$StringEncoder
 124:             4            128  java.util.Stack
 125:             4            128  java.util.concurrent.ConcurrentHashMap$ForwardingNode
 126:             3            120  java.io.BufferedInputStream
 127:             1            120  java.net.SocksSocketImpl
 128:             5            120  java.util.Collections$UnmodifiableRandomAccessList
 129:             5            120  sun.misc.FloatingDecimal$PreparedASCIIToBinaryBuffer
 130:             7            112  java.lang.Class$3
 131:             2            112  java.lang.Package
 132:             2            112  java.util.zip.ZipFile$ZipFileInflaterInputStream
 133:             2            112  sun.nio.cs.ISO_8859_1$Encoder
 134:             3             96  java.io.FileOutputStream
 135:             3             96  java.io.FilePermission
 136:             4             96  java.io.OutputStreamWriter
 137:             4             96  java.lang.RuntimePermission
 138:             2             96  java.lang.ThreadGroup
 139:             3             96  java.lang.ref.WeakReference
 140:             3             96  java.util.ArrayList$Itr
 141:             6             96  java.util.HashSet
 142:             2             96  java.util.ResourceBundle$BundleReference
 143:             2             96  java.util.zip.Inflater
 144:             1             96  sun.misc.Launcher$AppClassLoader
 145:             3             96  sun.misc.URLClassPath$JarLoader$2
 146:             3             96  sun.net.spi.DefaultProxySelector$NonProxyInfo
 147:             1             88  java.net.DualStackPlainSocketImpl
 148:             1             88  sun.misc.Launcher$ExtClassLoader
 149:             5             80  [Ljava.security.Principal;
 150:             2             80  [Lsun.util.locale.provider.LocaleProviderAdapter$Type;
 151:             2             80  java.io.ExpiringCache
 152:             5             80  java.io.FileInputStream$1
 153:             5             80  java.lang.ClassLoader$3
 154:             5             80  java.lang.ThreadLocal
 155:             1             80  java.net.URI
 156:             5             80  java.security.ProtectionDomain$Key
 157:             2             80  java.util.ServiceLoader$LazyIterator
 158:             2             80  sun.misc.FloatingDecimal$BinaryToASCIIBuffer
 159:             2             80  sun.misc.URLClassPath$1
 160:             2             80  sun.misc.URLClassPath$2
 161:             2             80  sun.util.locale.LanguageTag
 162:             3             72  [Ljava.lang.reflect.Method;
 163:             3             72  java.lang.ThreadLocal$ThreadLocalMap
 164:             3             72  java.net.Proxy$Type
 165:             3             72  java.util.Arrays$ArrayList
 166:             3             72  java.util.Collections$SynchronizedSet
 167:             1             72  java.util.ResourceBundle$RBClassLoader
 168:             3             72  java.util.concurrent.atomic.AtomicLong
 169:             3             72  sun.misc.FloatingDecimal$ExceptionalBinaryToASCIIBuffer
 170:             1             72  sun.util.locale.provider.JRELocaleProviderAdapter
 171:             1             64  [F
 172:             2             64  [Ljava.lang.Thread;
 173:             2             64  java.io.DataInputStream
 174:             2             64  java.io.FileNotFoundException
 175:             2             64  java.io.PrintStream
 176:             2             64  java.lang.ClassValue$Entry
 177:             2             64  java.lang.NoSuchMethodError
 178:             2             64  java.lang.VirtualMachineError
 179:             2             64  java.lang.ref.ReferenceQueue$Null
 180:             2             64  java.security.BasicPermissionCollection
 181:             2             64  java.security.Permissions
 182:             2             64  java.util.LinkedHashMap$LinkedEntryIterator
 183:             2             64  java.util.ServiceLoader
 184:             4             64  sun.reflect.ReflectionFactory$1
 185:             2             48  [Ljava.util.Enumeration;
 186:             2             48  java.io.BufferedOutputStream
 187:             2             48  java.io.ByteArrayOutputStream
 188:             2             48  java.io.File$PathStatus
 189:             3             48  java.io.FilePermission$1
 190:             2             48  java.io.FilePermissionCollection
 191:             2             48  java.io.FileReader
 192:             2             48  java.io.InputStreamReader
 193:             2             48  java.net.InetAddress$Cache
 194:             2             48  java.net.InetAddress$Cache$Type
 195:             1             48  java.net.SocketInputStream
 196:             2             48  java.net.URLClassLoader$3
 197:             2             48  java.nio.charset.CoderResult
 198:             3             48  java.nio.charset.CodingErrorAction
 199:             2             48  java.util.Date
 200:             1             48  java.util.Properties$LineReader
 201:             2             48  java.util.ServiceLoader$1
 202:             2             48  java.util.zip.ZStreamRef
 203:             2             48  sun.instrument.InstrumentationImpl$1
 204:             2             48  sun.misc.CompoundEnumeration
 205:             2             48  sun.misc.NativeSignalHandler
 206:             2             48  sun.misc.Signal
 207:             2             48  sun.net.www.MessageHeader
 208:             3             48  sun.net.www.ParseUtil
 209:             3             48  sun.net.www.protocol.jar.Handler
 210:             2             48  sun.nio.cs.Surrogate$Parser
 211:             1             48  sun.nio.cs.US_ASCII$Decoder
 212:             3             48  sun.reflect.ReflectionFactory$GetReflectionFactoryAction
 213:             1             48  sun.util.locale.provider.LocaleResources$ResourceReference
 214:             1             48  sun.util.resources.TimeZoneNames
 215:             1             48  sun.util.resources.en.TimeZoneNames_en
 216:             1             40  java.util.ResourceBundle$1
 217:             1             40  sun.nio.cs.StandardCharsets$Aliases
 218:             1             40  sun.nio.cs.StandardCharsets$Cache
 219:             1             40  sun.nio.cs.StandardCharsets$Classes
 220:             1             40  sun.nio.cs.ext.ExtendedCharsets
 221:             1             32  [Ljava.lang.OutOfMemoryError;
 222:             2             32  [Ljava.lang.StackTraceElement;
 223:             1             32  [Ljava.lang.ThreadGroup;
 224:             1             32  [Ljava.net.Proxy$Type;
 225:             1             32  java.io.ByteArrayInputStream
 226:             1             32  java.io.WinNTFileSystem
 227:             1             32  java.lang.ArithmeticException
 228:             2             32  java.lang.Boolean
 229:             2             32  java.lang.ClassLoader$2
 230:             1             32  java.lang.InternalError
 231:             1             32  java.lang.NullPointerException
 232:             1             32  java.net.InetAddress$InetAddressHolder
 233:             1             32  java.net.Socket
 234:             1             32  java.net.URI$Parser
 235:             2             32  java.net.URLClassLoader$3$1
 236:             2             32  java.nio.ByteOrder
 237:             2             32  java.util.Hashtable$KeySet
 238:             2             32  java.util.LinkedHashMap$LinkedEntrySet
 239:             2             32  java.util.LinkedHashMap$LinkedKeySet
 240:             2             32  java.util.concurrent.atomic.AtomicInteger
 241:             1             32  java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl
 242:             1             32  java.util.jar.Manifest$FastInputStream
 243:             1             32  sun.instrument.InstrumentationImpl
 244:             1             32  sun.misc.FloatingDecimal$ASCIIToBinaryBuffer
 245:             2             32  sun.misc.PostVMInitHook$1
 246:             1             32  sun.misc.URLClassPath$FileLoader$1
 247:             1             32  sun.net.spi.DefaultProxySelector$3
 248:             1             32  sun.nio.cs.StandardCharsets
 249:             2             32  sun.usagetracker.UsageTrackerClient$2
 250:             2             32  sun.usagetracker.UsageTrackerClient$3
 251:             1             32  sun.util.locale.provider.LocaleResources
 252:             1             32  sun.util.locale.provider.LocaleServiceProviderPool
 253:             1             24  [Ljava.io.File$PathStatus;
 254:             1             24  [Ljava.lang.ClassValue$Entry;
 255:             1             24  [Ljava.net.InetAddress$Cache$Type;
 256:             1             24  [Ljava.net.InetAddress;
 257:             1             24  [Ljava.security.ProtectionDomain;
 258:             1             24  [Lsun.launcher.LauncherHelper;
 259:             1             24  java.lang.Class$MethodArray
 260:             1             24  java.lang.ClassValue$Version
 261:             1             24  java.lang.invoke.MethodHandleImpl$4
 262:             1             24  java.lang.reflect.ReflectPermission
 263:             1             24  java.net.Inet4Address
 264:             1             24  java.net.Inet6AddressImpl
 265:             1             24  java.net.InetSocketAddress$InetSocketAddressHolder
 266:             1             24  java.net.Proxy
 267:             1             24  java.util.BitSet
 268:             1             24  java.util.Collections$EmptyMap
 269:             1             24  java.util.Collections$SetFromMap
 270:             1             24  java.util.Collections$UnmodifiableCollection$1
 271:             1             24  java.util.Locale$Cache
 272:             1             24  java.util.ResourceBundle$Control$CandidateListCache
 273:             1             24  java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl$1
 274:             1             24  java.util.jar.Manifest
 275:             1             24  sun.instrument.TransformerManager
 276:             1             24  sun.launcher.LauncherHelper
 277:             1             24  sun.misc.JarIndex
 278:             1             24  sun.misc.Launcher$AppClassLoader$1
 279:             1             24  sun.misc.URLClassPath$FileLoader
 280:             1             24  sun.nio.cs.ISO_8859_1
 281:             1             24  sun.nio.cs.ThreadLocalCoders$1
 282:             1             24  sun.nio.cs.ThreadLocalCoders$2
 283:             1             24  sun.nio.cs.US_ASCII
 284:             1             24  sun.nio.cs.UTF_16
 285:             1             24  sun.nio.cs.UTF_16BE
 286:             1             24  sun.nio.cs.UTF_16LE
 287:             1             24  sun.nio.cs.UTF_8
 288:             1             24  sun.nio.cs.ext.GBK
 289:             1             24  sun.reflect.NativeMethodAccessorImpl
 290:             1             24  sun.usagetracker.UsageTrackerClient$4
 291:             1             24  sun.util.locale.BaseLocale$Cache
 292:             1             24  sun.util.locale.provider.SPILocaleProviderAdapter$1
 293:             1             24  sun.util.locale.provider.TimeZoneNameProviderImpl
 294:             1             24  sun.util.resources.LocaleData$1
 295:             1             16  [Ljava.lang.Throwable;
 296:             1             16  [Ljava.security.cert.Certificate;
 297:             1             16  [Lsun.instrument.TransformerManager$TransformerInfo;
 298:             1             16  [Lsun.util.calendar.ZoneInfoFile$ZoneOffsetTransitionRule;
 299:             1             16  java.io.FileDescriptor$1
 300:             1             16  java.io.FileOutputStream$1
 301:             1             16  java.lang.CharacterDataLatin1
 302:             1             16  java.lang.ClassValue$Identity
 303:             1             16  java.lang.Compiler$1
 304:             1             16  java.lang.Runtime
 305:             1             16  java.lang.String$CaseInsensitiveComparator
 306:             1             16  java.lang.System$2
 307:             1             16  java.lang.SystemClassLoaderAction
 308:             1             16  java.lang.Terminator$1
 309:             1             16  java.lang.invoke.MemberName$Factory
 310:             1             16  java.lang.invoke.MethodHandleImpl$1
 311:             1             16  java.lang.invoke.MethodHandleImpl$2
 312:             1             16  java.lang.invoke.MethodHandleImpl$3
 313:             1             16  java.lang.invoke.MethodHandleStatics$1
 314:             1             16  java.lang.ref.Reference$1
 315:             1             16  java.lang.ref.Reference$Lock
 316:             1             16  java.lang.reflect.ReflectAccess
 317:             1             16  java.net.AbstractPlainSocketImpl$1
 318:             1             16  java.net.InetAddress$1
 319:             1             16  java.net.InetAddress$2
 320:             1             16  java.net.InetSocketAddress
 321:             1             16  java.net.PlainSocketImpl$1
 322:             1             16  java.net.Socket$2
 323:             1             16  java.net.SocksSocketImpl$3
 324:             1             16  java.net.URLClassLoader$7
 325:             1             16  java.nio.Bits$1
 326:             1             16  java.nio.charset.Charset$ExtendedProviderHolder$1
 327:             1             16  java.nio.charset.CoderResult$1
 328:             1             16  java.nio.charset.CoderResult$2
 329:             1             16  java.security.ProtectionDomain$2
 330:             1             16  java.security.ProtectionDomain$JavaSecurityAccessImpl
 331:             1             16  java.util.Collections$EmptyIterator
 332:             1             16  java.util.Collections$EmptyList
 333:             1             16  java.util.Collections$EmptySet
 334:             1             16  java.util.Hashtable$EntrySet
 335:             1             16  java.util.ResourceBundle$Control
 336:             1             16  java.util.ResourceBundle$RBClassLoader$1
 337:             1             16  java.util.TimeZone$1
 338:             1             16  java.util.WeakHashMap$KeySet
 339:             1             16  java.util.concurrent.atomic.AtomicBoolean
 340:             1             16  java.util.jar.Attributes
 341:             1             16  java.util.jar.JavaUtilJarAccessImpl
 342:             1             16  java.util.zip.ZipFile$1
 343:             1             16  sun.misc.ASCIICaseInsensitiveComparator
 344:             1             16  sun.misc.FloatingDecimal$1
 345:             1             16  sun.misc.Launcher
 346:             1             16  sun.misc.Launcher$BootClassPathHolder$1
 347:             1             16  sun.misc.Launcher$ExtClassLoader$1
 348:             1             16  sun.misc.Launcher$Factory
 349:             1             16  sun.misc.Perf
 350:             1             16  sun.misc.Perf$GetPerfAction
 351:             1             16  sun.misc.PostVMInitHook$2
 352:             1             16  sun.misc.Unsafe
 353:             1             16  sun.net.NetProperties$1
 354:             1             16  sun.net.spi.DefaultProxySelector
 355:             1             16  sun.net.spi.DefaultProxySelector$1
 356:             1             16  sun.net.www.protocol.file.Handler
 357:             1             16  sun.reflect.DelegatingMethodAccessorImpl
 358:             1             16  sun.reflect.ReflectionFactory
 359:             1             16  sun.security.action.GetBooleanAction
 360:             1             16  sun.usagetracker.UsageTrackerClient
 361:             1             16  sun.util.calendar.Gregorian
 362:             1             16  sun.util.calendar.ZoneInfoFile$1
 363:             1             16  sun.util.calendar.ZoneInfoFile$Checksum
 364:             1             16  sun.util.locale.provider.AuxLocaleProviderAdapter$NullProvider
 365:             1             16  sun.util.locale.provider.JRELocaleProviderAdapter$1
 366:             1             16  sun.util.locale.provider.SPILocaleProviderAdapter
 367:             1             16  sun.util.locale.provider.TimeZoneNameUtility$TimeZoneNameGetter
 368:             1             16  sun.util.resources.LocaleData
 369:             1             16  sun.util.resources.LocaleData$LocaleDataResourceBundleControl
Total         24100       48649392
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

# 其他作用

jmap -permstat pid

查看系统的 ClassLoader 信息。

jmap -finalizerinfo pid

查看堆积在 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 &lt;option&gt; <dumpfile>

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

示例

在 jmap 的示例中,我们在 D 盘生成了 5 个 dump 文件,接下来我们直接在命令行使用 jhat 命令,打开 dump 文件。

jhat d:\3.hprof
1

image-20220131212831537

打开浏览器访问 http://localhost:7000/

image-20220131212920407

Execute Object Query Language (OQL) query 可以利用 OQL 查询想要的类,如

select s from java.lang.String s where s.value.length > 100
1

# jstack:打印 JVM 中线程快照

官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jstack.html

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

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

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

  • 死锁,Deadlock(重点关注)
  • 等待资源,Waiting on condition(重点关注)
  • 等待获取监视器,Waiting on monitor entry(重点关注)
  • 阻塞,Blocked(重点关注)
  • 执行中,Runnable
  • 暂停,Suspended
  • 对象等待中,Object.wait()TIMED_WAITING
  • 停止,Parked
option 参数 作用
-F 当正常输出的请求不被响应时,强制输出线程堆栈
-l 除堆栈外,显示关于锁的附加信息
-m 如果调用本地方法的话,可以显示 C/C++ 的堆栈

代码示例(死锁)

public class ThreadDeadLock {

    public static void main(String[] args) {

        StringBuilder s1 = new StringBuilder();
        StringBuilder s2 = new StringBuilder();

        new Thread(){
            @Override
            public void run() {
                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(new Runnable() {
            @Override
            public void run() {
                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();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                Map<Thread, StackTraceElement[]> all = Thread.getAllStackTraces();//追踪当前进程中的所有的线程
                Set<Map.Entry<Thread, StackTraceElement[]>> entries = all.entrySet();
                for(Map.Entry<Thread, StackTraceElement[]> en : entries){
                    Thread t = en.getKey();
                    StackTraceElement[] v = en.getValue();
                    System.out.println("【Thread name is :" + t.getName() + "】");
                    for(StackTraceElement s : v){
                        System.out.println("\t" + s.toString());
                    }
                }
            }
        }).start();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

运行后,在命令行使用该命令:

D:\>jps
8912
22164 ThreadDeadLock
18424 Jps
5084 Launcher

D:\>jstack 22164
1
2
3
4
5
6
7

输出的代码:








 






 























































































 






















2022-01-31 21:51:08
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.231-b11 mixed mode):

"DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x0000000002b12800 nid=0x5b50 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001e041800 nid=0x9bc waiting for monitor entry [0x000000001fd1f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.youngkbt.jstack.ThreadDeadLock$2.run(ThreadDeadLock.java:63)
        - waiting to lock <0x000000076ba1e2f0> (a java.lang.StringBuilder)
        - locked <0x000000076ba1e338> (a java.lang.StringBuilder)
        at java.lang.Thread.run(Thread.java:748)

"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001e03b800 nid=0x52f8 waiting for monitor entry [0x000000001fc1f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.youngkbt.jstack.ThreadDeadLock$1.run(ThreadDeadLock.java:35)
        - waiting to lock <0x000000076ba1e338> (a java.lang.StringBuilder)
        - locked <0x000000076ba1e2f0> (a java.lang.StringBuilder)

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

"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001df47000 nid=0x533c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001df44800 nid=0x4ef8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

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

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

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001df35800 nid=0x2da8 runnable [0x000000001f4fe000]
   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 <0x000000076b904d88> (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 <0x000000076b904d88> (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=0x000000001de9e000 nid=0xac waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

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

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001c7c3800 nid=0x1610 in Object.wait() [0x000000001f1df000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076b788ed8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x000000076b788ed8> (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=0x000000001de83000 nid=0x31e4 in Object.wait() [0x000000001f0de000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076b786c00> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x000000076b786c00> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x000000001de62800 nid=0x575c runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002b28800 nid=0x1768 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002b2a000 nid=0x97c runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002b2c000 nid=0x4364 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002b2d800 nid=0x4608 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002b2f800 nid=0x4f38 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002b32000 nid=0xb80 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002b35000 nid=0xce4 runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002b36000 nid=0x5510 runnable

"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x0000000002b37800 nid=0x193c runnable

"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x0000000002b38800 nid=0x1010 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x000000001e008000 nid=0x1144 waiting on condition

JNI global references: 12


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000001e044d58 (object 0x000000076ba1e2f0, a java.lang.StringBuilder),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000001c7c2dc8 (object 0x000000076ba1e338, a java.lang.StringBuilder),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at com.youngkbt.jstack.ThreadDeadLock$2.run(ThreadDeadLock.java:63)
        - waiting to lock <0x000000076ba1e2f0> (a java.lang.StringBuilder)
        - locked <0x000000076ba1e338> (a java.lang.StringBuilder)
        at java.lang.Thread.run(Thread.java:748)
"Thread-0":
        at com.youngkbt.jstack.ThreadDeadLock$1.run(ThreadDeadLock.java:35)
        - waiting to lock <0x000000076ba1e338> (a java.lang.StringBuilder)
        - locked <0x000000076ba1e2f0> (a java.lang.StringBuilder)

Found 1 deadlock.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

部分图:(可以看出 BLOCKED 进入死锁阻塞)

image-20220131215206187

其他代码示例:

因为内容结果太长,所以只给代码,在命令行的输入可以自行练习查看(也可以使用 option 参数查看额外内容)。

线程睡眠代码:

public class TreadSleepTest {
    public static void main(String[] args) {
        System.out.println("hello - 1");
        try {
            Thread.sleep(1000 * 60 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("hello - 2");
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13

线程同步代码:

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 implements Runnable {
    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++;

                } else {
                    break;
                }
            }
        }
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

在控制台输出结果的代码:

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[]> en : entries){
            Thread t = en.getKey();
            StackTraceElement[] v = en.getValue();
            System.out.println("【Thread name is :" + t.getName() + "】");
            for(StackTraceElement s : v){
                System.out.println("\t" + s.toString());
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# jcmd:多功能命令行

官方帮助文档:https://docs.oracle.com/en/java/javase/11/tools/jcmd.html

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

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

image-20220131220953560

常用命令

jcmd -l:列出所有的 JVM 进程,可以替换 jps -m。如图:

image-20220131220313703

jcmd 进程号 help:针对指定的进程,列出支持的所有具体命令,如图:

image-20220131220228871

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

其他命令(可以替换上面学的命令)

  • Thread.print 可以替换 jstack 指令
  • GC.class_histogram 可以替换 jmap 中的 -histo 操作
  • GC.heap_dump 可以替换 jmap 中的 -dump 操作
  • GC.run 可以查看 GC 的执行情况
  • VM.uptime 可以查看程序的总执行时间,可以替换 jstat 指令中的 -t 操作
  • VM.system_properties 可以替换 jinfo -sysprops 进程 id
  • VM.flags 可以获取 JVM 的配置参数信息

替换 jmap 中的 -histo 操作示例(两者结果一样):

jcmd 10561 GC.class_histogram 
1

替换 jmap 中的 -dump 操作,即该命令也会生成一个 dump 文件:

jcmd 10561 GC.heap_dump d:\m.hprof
1

# jstatd:远程主机信息收集

之前的指令只涉及到监控本机的 Java 应用程序,而在这些工具中,一些监控工具也支持对远程计算机的监控(如 jps、jstat)。为了启用远程监控,则需要配合使用 jstatd 工具。命令 jstatd 是一个 RMI 服务端程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信。jstatd 服务器将本机的 Java 应用程序信息传递到远程计算机。

image-20220131221034831

更新时间: 2024/01/17, 05:48:13
最近更新
01
JVM调优
12-10
02
jenkins
12-10
03
Arthas
12-10
更多文章>