JVM系列(七) - JVM线上监控工具

前言

经过上一篇的 JVM 垃圾回收知识,咱们了解了 JVM 具体的 垃圾回收算法 和几种 垃圾回收器。理论是指导实践的工具,有了理论指导,定位问题的时候,知识和经验是关键基础,数据能够为咱们提供依据。html

在线上咱们常常会碰见以下几个问题:java

  • 内存泄露;
  • 某个进程忽然 CPU 飙升;
  • 线程死锁;
  • 响应变慢。

若是遇到了以上这种问题,在 线下环境 能够有各类 可视化的本地工具 支持查看。可是一旦到 线上环境,就没有这么多的 本地调试工具 支持,咱们该如何基于 监控工具 来进行定位问题?算法

咱们通常会基于 数据收集 来定位问题,而数据的收集离不开 监控工具 的处理,好比:运行日志异常堆栈GC 日志线程快照堆内存快照 等。为了解决以上问题,咱们经常使用的 JVM 性能调优监控工具 大体有:jpsjstatjstackjmapjhathprofjinfospring

正文

若是想要查看 Java 进程中 线程堆栈 的信息,能够选择 jstack 命令。若是要查看 堆内存,可使用 jmap 导出并使用 jhat 来进行分析,包括查看 类的加载信息GC 算法对象 的使用状况等。可使用 jstat 来对 JVM 进行 统计监测,包括查看各个 区内存 和 GC 的状况,还可使用 hprof 查看 CPU 使用率,统计 堆内存 使用状况。下面会详细的介绍这几个工具的用法。数据库

JVM常见监控工具 & 指令

1. jps进程监控工具

jps 是用于查看有权访问的 hotspot 虚拟机 的进程。当未指定 hostid 时,默认查看 本机 jvm 进程,不然查看指定的 hostid 机器上的 jvm 进程,此时 hostid 所指机器必须开启 jstatd 服务。编程

jps 能够列出 jvm 进程 lvmid主类类名main 函数参数, jvm 参数,jar 名称等信息。后端

命令格式以下:api

1
2
3
4
5
usage: jps [-help]
jps [-q] [-mlvV] [<hostid>]

Definitions:
<hostid>: <hostname>[:<port>]

参数含义以下:数组

  • -q: 不输出 类名称Jar 名称 和传入 main 方法的 参数
  • -l: 输出 main 类或 Jar 的 全限定名称
  • -m: 输出传入 main 方法的 参数
  • -v: 输出传入 JVM 的参数。

2. jinfo配置信息查看工具

jinfoJVM Configuration info)这个命令做用是实时查看和调整 虚拟机运行参数。以前的 jps -v 命令只能查看到显示 指定的参数,若是想要查看 未显示 的参数的值就要使用 jinfo 命令。浏览器

1
2
3
4
5
6
7
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)

参数含义以下:

  • pid:本地 jvm 服务的进程 ID
  • executable core:打印 堆栈跟踪 的核心文件;
  • remote server IP/hostname:远程 debug 服务的 主机名 或 IP 地址;
  • server id:远程 debug 服务的 进程 ID

参数选项说明以下:

参数 参数含义
flag 输出指定 args 参数的值
flags 不须要 args 参数,输出全部 JVM 参数的值
sysprops 输出系统属性,等同于 System.getProperties()
  • 查看正在运行的 jvm 进程的 扩展参数
1
2
3
4
5
6
7
$ jinfo -flags 31983 
Attaching to process ID 31983, please wait…
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b14
Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=20971520 -XX:MaxHeapFreeRatio=90 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=2097152 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=2097152 -XX:OldSize=18874368 -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
Command line: -Xmx20m -Xms20m -Xmn2m -javaagent:/opt/idea-IU-181.4668.68/lib/idea_rt.jar=34989:/opt/idea-IU-181.4668.68/bin -Dfile.encoding=UTF-8
  • 查看正在运行的 jvm 进程的全部 参数信息
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
$ jinfo 31983
Attaching to process ID 31983, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b14
Java System Properties:

java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.91-b14
sun.boot.library.path = /opt/jdk1.8.0_91/jre/lib/amd64
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = CN
user.dir = /home/linchen/projects
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_91-b14
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /opt/jdk1.8.0_91/jre/lib/endorsed
java.io.tmpdir = /tmp
line.separator =

java.vm.specification.vendor = Oracle Corporation
os.name = Linux
sun.jnu.encoding = UTF-8
java.library.path = /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 4.15.0-24-generic
user.home = /home/linchen
user.timezone =
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
user.name = linchen
java.class.path = /opt/jdk1.8.0_91/jre/lib/charsets.jar:/opt/jdk1.8.0_91/jre/lib/deploy.jar:/opt/jdk1.8.0_91/jre/lib/ext/cldrdata.jar:/opt/jdk1.8.0_91/jre/lib/ext/dnsns.jar:/opt/jdk1.8.0_91/jre/lib/ext/jaccess.jar:/opt/jdk1.8.0_91/jre/lib/ext/jfxrt.jar:/opt/jdk1.8.0_91/jre/lib/ext/localedata.jar:/opt/jdk1.8.0_91/jre/lib/ext/nashorn.jar:/opt/jdk1.8.0_91/jre/lib/ext/sunec.jar:/opt/jdk1.8.0_91/jre/lib/ext/sunjce_provider.jar:/opt/jdk1.8.0_91/jre/lib/ext/sunpkcs11.jar:/opt/jdk1.8.0_91/jre/lib/ext/zipfs.jar:/opt/jdk1.8.0_91/jre/lib/javaws.jar:/opt/jdk1.8.0_91/jre/lib/jce.jar:/opt/jdk1.8.0_91/jre/lib/jfr.jar:/opt/jdk1.8.0_91/jre/lib/jfxswt.jar:/opt/jdk1.8.0_91/jre/lib/jsse.jar:/opt/jdk1.8.0_91/jre/lib/management-agent.jar:/opt/jdk1.8.0_91/jre/lib/plugin.jar:/opt/jdk1.8.0_91/jre/lib/resources.jar:/opt/jdk1.8.0_91/jre/lib/rt.jar:/home/linchen/IdeaProjects/core_java/target/classes:/home/linchen/.m2/repository/io/netty/netty-all/4.1.7.Final/netty-all-4.1.7.Final.jar:/home/linchen/.m2/repository/junit/junit/4.12/junit-4.12.jar:/home/linchen/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:/home/linchen/.m2/repository/com/lmax/disruptor/3.3.0/disruptor-3.3.0.jar:/home/linchen/.m2/repository/com/rabbitmq/amqp-client/5.3.0/amqp-client-5.3.0.jar:/home/linchen/.m2/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar:/opt/idea-IU-181.4668.68/lib/idea_rt.jar
java.vm.specification.version = 1.8
sun.arch.data.model = 64
sun.java.command = com.own.learn.jvm.JinfoTest
java.home = /opt/jdk1.8.0_91/jre
user.language = zh
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.X11.XToolkit
java.vm.info = mixed mode
java.version = 1.8.0_91
java.ext.dirs = /opt/jdk1.8.0_91/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path = /opt/jdk1.8.0_91/jre/lib/resources.jar:/opt/jdk1.8.0_91/jre/lib/rt.jar:/opt/jdk1.8.0_91/jre/lib/sunrsasign.jar:/opt/jdk1.8.0_91/jre/lib/jsse.jar:/opt/jdk1.8.0_91/jre/lib/jce.jar:/opt/jdk1.8.0_91/jre/lib/charsets.jar:/opt/jdk1.8.0_91/jre/lib/jfr.jar:/opt/jdk1.8.0_91/jre/classes
java.vendor = Oracle Corporation
file.separator = /
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.desktop = gnome
sun.cpu.isalist =

VM Flags:
Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=20971520 -XX:MaxHeapFreeRatio=90 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=2097152 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=2097152 -XX:OldSize=18874368 -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
Command line: -Xmx20m -Xms20m -Xmn2m -javaagent:/opt/idea-IU-181.4668.68/lib/idea_rt.jar=34989:/opt/idea-IU-181.4668.68/bin -Dfile.encoding=UTF-8
  • 查看正在运行的 jvm 进程的 环境变量信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ jinfo -sysprops 31983 
Attaching to process ID 31983, please wait…
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b14
java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.91-b14
sun.boot.library.path = /opt/jdk1.8.0_91/jre/lib/amd64
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = CN
user.dir = /home/linchen/projects
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_91-b14
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /opt/jdk1.8.0_91/jre/lib/endorsed
java.io.tmpdir = /tmp
line.separator =

2. jstat信息统计监控工具

jstat 是用于识别 虚拟机 各类 运行状态信息 的命令行工具。它能够显示 本地 或者 远程虚拟机 进程中的 类装载内存垃圾收集jit 编译 等运行数据,它是 线上 定位 jvm 性能 的首选工具。

jstat 工具提供以下的 jvm 监控功能:

  1. 类的加载 及 卸载 的状况;
  2. 查看 新生代老生代 及 元空间MetaSpace)的 容量 及使用状况;
  3. 查看 新生代老生代 及 元空间MetaSpace)的 垃圾回收状况,包括垃圾回收的 次数,垃圾回收所占用的 时间
  4. 查看 新生代 中 Eden 区及 Survior 区中 容量 及 分配状况 等。

命令格式以下:

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

参数含义以下:

  • option: 参数选项。
    • -t: 能够在打印的列加上 timestamp 列,用于显示系统运行的时间。
    • -h: 能够在 周期性数据 的时候,能够在指定输出多少行之后输出一次 表头
  • vmid: Virtual Machine ID(进程的 pid)。
  • lines: 表头 与 表头 的间隔行数。
  • interval: 执行每次的 间隔时间,单位为 毫秒
  • count: 用于指定输出记录的 次数,缺省则会一直打印。

参数选项说明以下:

  • class: 显示 类加载 ClassLoad 的相关信息;
  • compiler: 显示 JIT 编译 的相关信息;
  • gc: 显示和 gc 相关的 堆信息
  • gccapacity: 显示 各个代 的 容量 以及 使用状况
  • gcmetacapacity: 显示 元空间 metaspace 的大小;
  • gcnew: 显示 新生代 信息;
  • gcnewcapacity: 显示 新生代大小 和 使用状况
  • gcold: 显示 老年代 和 永久代 的信息;
  • gcoldcapacity: 显示 老年代 的大小;
  • gcutil: 显示 垃圾回收信息
  • gccause: 显示 垃圾回收 的相关信息(同 -gcutil),同时显示 最后一次 或 当前 正在发生的垃圾回收的 诱因
  • printcompilation: 输出 JIT 编译 的方法信息;

2.1. class

显示和监视 类装载卸载数量总空间 以及 耗费的时间

1
2
3
$ jstat -class 8615
Loaded Bytes Unloaded Bytes Time
7271 13325.8 1 0.9 2.98

参数列表及含义以下:

参数 参数含义
Loaded 已经装载的类的数量
Bytes 装载类所占用的字节数
Unloaded 已经卸载类的数量
Bytes 卸载类的字节数
Time 装载和卸载类所花费的时间

2.2. compiler

显示虚拟机 实时编译JIT)的 次数 和 耗时 等信息。

1
2
3
$ jstat -compiler 8615
Compiled Failed Invalid Time FailedType FailedMethod
3886 0 0 1.29 0

参数列表及含义以下:

参数 参数含义
Compiled 编译任务执行数量
Failed 编译任务执行失败数量
Invalid 编译任务执行失效数量
Time 编译任务消耗时间
FailedType 最后一个编译失败任务的类型
FailedMethod 最后一个编译失败任务所在的类及方法

2.3. gc

显示 垃圾回收gc)相关的 堆信息,查看 gc 的 次数 及 时间

1
2
3
$ jstat -gc 8615
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
20480.0 10752.0 0.0 0.0 262128.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131

好比下面输出的是 GC 信息,采样 时间间隔 为 250ms,采样数为 4

1
2
3
4
5
6
$ jstat -gc 8615 250 4
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
20480.0 10752.0 0.0 0.0 262144.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131
20480.0 10752.0 0.0 0.0 262872.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131
20480.0 10752.0 0.0 0.0 262720.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131
20480.0 10752.0 0.0 0.0 262446.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131

参数列表及含义以下:

参数 参数含义
S0C 年轻代中第一个 survivor 的容量
S1C 年轻代中第二个 survivor 的容量
S0U 年轻代中第一个 survivor 目前已使用空间
S1U 年轻代中第二个 survivor 目前已使用空间
EC 年轻代中 Eden 的容量
EU 年轻代中 Eden 目前已使用空间
OC 老年代的容量
OU 老年代目前已使用空间
MC 元空间 metaspace 的容量
MU 元空间 metaspace 目前已使用空间
YGC 从应用程序启动到采样时 年轻代 中 gc 次数
YGCT 从应用程序启动到采样时 年轻代 中 gc 所用时间
FGC 从应用程序启动到采样时 老年代 中 gc 次数
FGCT 从应用程序启动到采样时 老年代 中 gc 所用时间
GCT 从应用程序启动到采样时 gc 用的 总时间

2.4. gccapacity

显示 虚拟机内存 中三代 年轻代young),老年代old),元空间metaspace)对象的使用和占用大小。

1
2
3
$ jstat -gccapacity 8615
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
87040.0 1397760.0 372736.0 20480.0 10752.0 262144.0 175104.0 2796544.0 165376.0 165376.0 0.0 1079296.0 35456.0 0.0 1048576.0 4992.0 5 2

参数列表及含义以下:

参数 参数含义
NGCMN 年轻代的 初始化(最小)容量
NGCMX 年轻代的 最大容量
NGC 年轻代 当前的容量
S0C 年轻代中 第一个 survivor 区的容量
S1C 年轻代中 第二个 survivor 区的容量
EC 年轻代中 Eden伊甸园)的容量
OGCMN 老年代中 初始化(最小)容量
OGCMX 老年代的 最大容量
OGC 老年代 当前新生成 的容量
OC 老年代的容量大小
MCMN 元空间 的 初始化容量
MCMX 元空间 的 最大容量
MC 元空间 当前 新生成 的容量
CCSMN 最小 压缩类空间大小
CCSMX 最大 压缩类空间大小
CCSC 当前 压缩类空间大小
YGC 从应用程序启动到采样时 年轻代 中的 gc 次数
FGC 从应用程序启动到采样时 老年代 中的 gc 次数

2.5. gcmetacapacity

显示 元空间metaspace)中 对象 的信息及其占用量。

1
2
3
$ jstat -gcmetacapacity 8615
MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT
0.0 1079296.0 35456.0 0.0 1048576.0 4992.0 5 2 0.075 0.131

参数列表及含义以下:

参数 参数含义
MCMN 最小 元数据空间容量
MCMX 最大 元数据空间容量
MC 当前 元数据空间容量
CCSMN 最小压缩 类空间容量
CCSMX 最大压缩 类空间容量
CCSC 当前 压缩类空间容量
YGC 从应用程序启动到采样时 年轻代 中 gc 次数
FGC 从应用程序启动到采样时 老年代 中 gc 次数
FGCT 从应用程序启动到采样时 老年代 gc 所用时间
GCT 从应用程序启动到采样时 gc 用的 总时间

2.6. gcnew

显示 年轻代对象 的相关信息,包括两个 survivor 区和 一个 Eden 区。

1
2
3
$ jstat -gcnew 8615
S0C S1C S0U S1U TTv MTT DSS EC EU YGC YGCT
20480.0 10752.0 0.0 0.0 6 15 20480.0 262144.0 131406.0 5 0.056

参数列表及含义以下:

参数 参数含义
S0C 年轻代中第一个 survivor 的容量
S1C 年轻代中第二个 survivor 的容量
S0U 年轻代中第一个 survivor 目前已使用空间
S1U 年轻代中第二个 survivor 目前已使用空间
TT 持有次数限制
MTT 最大持有次数限制
DSS 指望的 幸存区 大小
EC 年轻代中 Eden 的容量
EU 年轻代中 Eden 目前已使用空间
YGC 从应用程序启动到采样时 年轻代 中 gc 次数
YGCT 从应用程序启动到采样时 年轻代 中 gc 所用时间

2.7. gcnewcapacity

查看 年轻代 对象的信息及其占用量。

1
2
3
$ jstat -gcnewcapacity 8615
NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
87040.0 1397760.0 372736.0 465920.0 20480.0 465920.0 10752.0 1396736.0 262144.0 5 2

参数列表及含义以下:

参数 参数含义
NGCMN 年轻代中初始化(最小)的大小
NGCMX 年轻代的最大容量
NGC 年轻代中当前的容量
S0CMX 年轻代中第一个 survivor 的最大容量
S0C 年轻代中第一个 survivor的容量
S1CMX 年轻代中第二个 survivor 的最大容量
S1C 年轻代中第二个 survivor 的容量
ECMX 年轻代中 Eden 的最大容量
EC 年轻代中 Eden 的容量
YGC 从应用程序启动到采样时 年轻代 中 gc 次数
FGC 从应用程序启动到采样时 老年代 中 gc 次数

2.8. gcold

显示 老年代对象 的相关信息。

1
2
3
$ jstat -gcold 8615
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
35456.0 33931.0 4992.0 4582.0 165376.0 24093.7 5 2 0.075 0.131

参数列表及含义以下:

参数 参数含义
MC 元空间metaspace)的容量
MU 元空间metaspace)目前已使用空间
CCSC 压缩类空间大小
CCSU 压缩类空间 使用 大小
OC 老年代 的容量
OU 老年代 目前已使用空间
YGC 从应用程序启动到采样时 年轻代 中 gc 次数
FGC 从应用程序启动到采样时 老年代 中 gc 次数
FGCT 从应用程序启动到采样时 老年代 gc 所用时间
GCT 从应用程序启动到采样时 gc 用的 总时间

2.9. gcoldcapacity

查看 老年代 对象的信息及其占用量。

1
2
3
$ jstat -gcoldcapacity 8615
OGCMN OGCMX OGC OC YGC FGC FGCT GCT
175104.0 2796544.0 165376.0 165376.0 5 2 0.075 0.131

参数列表及含义以下:

参数 参数含义
OGCMN 老年代 中初始化(最小)的大小
OGCMX 老年代 的最大容量
OGC 老年代 当前新生成的容量
OC 老年代 的容量
YGC 从应用程序启动到采样时 年轻代 中 gc 的次数
FGC 从应用程序启动到采样时 老年代 中 gc 的次数
FGCT 从应用程序启动到采样时 老年代 中 gc 所用时间
GCT 从应用程序启动到采样时 gc 用的 总时间

2.10. gcutil

显示 垃圾回收gc)过程当中的信息,包括各个 内存的使用占比,垃圾回收 时间 和回收 次数

1
2
3
$ jstat -gcutil 8615
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 50.13 14.57 95.70 91.79 5 0.056 2 0.075 0.131

参数列表及含义以下:

参数 参数含义
S0 年轻代中 第一个 survivor 区 已使用 的占当前容量百分比
S1 年轻代中 第二个 survivor 区 已使用 的占当前容量百分比
E 年轻代中 Eden 区 已使用 的占当前容量百分比
O 老年代 中 已使用 的占当前容量百分比
M 元空间metaspace)中 已使用 的占当前容量百分比
YGC 从应用程序启动到采样时 年轻代 中 gc 次数
YGCT 从应用程序启动到采样时 年轻代 中 gc 所用时间
FGC 从应用程序启动到采样时 老年代 gc 次数
FGCT 从应用程序启动到采样时 老年代 gc 所用时间
GCT 从应用程序启动到采样时 gc 用的 总时间

3. jmap堆内存统计工具

jmap (JVM Memory Map) 命令用来查看 堆内存 使用情况,通常结合 jhat 使用,用于生成 heap dump 文件。jmap 不只能生成 dump 文件,还能够查询 finalize 执行队列Java  和 元空间 metaspace 的详细信息,如当前 使用率、当前使用的是哪一种 收集器 等等。

若是不使用这个命令,还可使用 -XX:+HeapDumpOnOutOfMemoryError 参数来让虚拟机出现 OOM 的时候,自动生成 dump 文件。

命令格式以下:

1
2
3
4
5
6
7
Usage:
jmap [option] <pid>
(to connect to running process)
jmap [option] <executable <core>
(to connect to a core file)
jmap [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)

参数含义以下:

  • pid:本地 jvm 服务的进程 ID
  • executable core:打印 堆栈跟踪 的核心文件;
  • remote server IP/hostname:远程 debug 服务的 主机名 或 IP 地址;
  • server id:远程 debug 服务的 进程 ID

参数选项说明以下:

参数 参数含义
heap 显示  中的摘要信息
histo 显示  中对象的统计信息
histo[:live] 只显示  中 存活对象 的统计信息
clstats 显示 类加载 的统计信息
finalizerinfo 显示在 F-Queue 队列 等待 Finalizer 线程执行 finalizer 方法的对象
dump 导出内存转储快照

注意:dump 内存快照分析基本上包含了 histoclstatsfinalizerinfo 等功能。

3.1. heap

显示  中的摘要信息。包括 堆内存 的使用状况,正在使用的 GC 算法堆配置参数 和 各代中堆内存 使用状况。能够用此来判断内存目前的 使用状况 以及 垃圾回收 状况。

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
$ jmap -heap 11368
Attaching to process ID 11368, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.101-b13

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

Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 2684354560 (2560.0MB)
NewSize = 1073741824 (1024.0MB)
MaxNewSize = 1073741824 (1024.0MB)
OldSize = 1610612736 (1536.0MB)
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 = 852492288 (813.0MB)
used = 420427144 (400.95056915283203MB)
free = 432065144 (412.04943084716797MB)
49.31741317993014% used
From Space:
capacity = 113770496 (108.5MB)
used = 2299712 (2.19317626953125MB)
free = 111470784 (106.30682373046875MB)
2.021360617079493% used
To Space:
capacity = 107479040 (102.5MB)
used = 0 (0.0MB)
free = 107479040 (102.5MB)
0.0% used
PS Old Generation
capacity = 1610612736 (1536.0MB)
used = 50883368 (48.526161193847656MB)
free = 1559729368 (1487.4738388061523MB)
3.1592552860577903% used

27595 interned Strings occupying 3138384 bytes.

这里主要对 heap configuration 的参数列表说明一下:

参数 对应启动参数 参数含义
MinHeapFreeRatio -XX:MinHeapFreeRatio JVM堆最小空闲比率(default 40)
MaxHeapFreeRatio -XX:MaxHeapFreeRatio JVM堆最大空闲比率(default 70)
MaxHeapSize XX:Xmx JVM堆的最大大小
NewSize -XX:NewSize JVM堆新生代的默认(初始化)大小
MaxNewSize -XX:MaxNewSize JVM堆新生代的最大大小
OldSize -XX:OldSize JVM堆老年代的默认(初始化)大小
NewRatio -XX:NewRatio JVM堆新生代和老年代的大小比例
SurvivorRatio -XX:SurvivorRatio JVM堆年轻代中Eden区与Survivor区的大小比值
MetaspaceSize -XX:MetaspaceSize JVM元空间(metaspace)初始化大小
MaxMetaspaceSize -XX:MaxMetaspaceSize JVM元空间(metaspace)最大大小
CompressedClass SpaceSize -XX:CompressedClass SpaceSize JVM类指针压缩空间大小, 默认为1G
G1HeapRegionSize -XX:G1HeapRegionSize 使用G1垃圾回收器时单个Region的大小,取值为1M至32M

3.2. histo

打印堆的 对象统计,包括 对象实例数内存大小 等等。由于在 histo:live 前会进行 full gc,若是带上 live 则只统计 活对象。不加 live 的堆大小要大于加 live 堆的大小。

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
$ jmap -histo:live 12498
num #instances #bytes class name
----------------------------------------------
1: 50358 7890344 [C
2: 22887 2014056 java.lang.reflect.Method
3: 3151 1485512 [B
4: 49267 1182408 java.lang.String
5: 7836 871384 java.lang.Class
6: 24149 772768 java.util.concurrent.ConcurrentHashMap$Node
7: 20785 482256 [Ljava.lang.Class;
8: 8357 435248 [Ljava.lang.Object;
9: 10035 401400 java.util.LinkedHashMap$Entry
10: 4803 369488 [Ljava.util.HashMap$Node;
11: 10763 344416 java.util.HashMap$Node
12: 5205 291480 java.util.LinkedHashMap
13: 3055 219960 java.lang.reflect.Field
14: 120 193408 [Ljava.util.concurrent.ConcurrentHashMap$Node;
15: 11224 179584 java.lang.Object
16: 1988 146152 [Ljava.lang.reflect.Method;
17: 3036 145728 org.aspectj.weaver.reflect.ShadowMatchImpl
18: 1771 141680 java.lang.reflect.Constructor
19: 4903 117672 org.springframework.core.MethodClassKey
20: 3263 104416 java.lang.ref.WeakReference
21: 2507 100280 java.lang.ref.SoftReference
22: 2523 97600 [I
23: 3036 97152 org.aspectj.weaver.patterns.ExposedState
24: 2072 95280 [Ljava.lang.String;
25: 954 91584 org.springframework.beans.GenericTypeAwarePropertyDescriptor
26: 1633 91448 java.lang.Class$ReflectionData
27: 3142 90520 [Z
28: 1671 80208 java.util.HashMap
29: 3244 77856 java.util.ArrayList
30: 3037 72880 [Lorg.aspectj.weaver.ast.Var;
31: 1809 72360 java.util.WeakHashMap$Entry
32: 1967 62944 java.util.LinkedList

其中,class name 是 对象类型,对象 缩写类型 与 真实类型 的对应说明以下:

对象缩写类型 对象真实类型
B byte
C char
D double
F float
I int
J long
Z boolean
[ 数组,如[I表示int[]
[L+类名 其余对象

3.3. dump

dump 用于导出内存转储快照。经常使用的方式是经过 jmap 把进程 内存使用状况 dump 到文件中,再用 jhat 分析查看。jmap 进行 dump 的命令格式以下:

1
jmap -dump:format=b,file=dumpFileName

参数含义以下:

参数 参数含义
dump 堆到文件
format 指定输出格式
live 指明是活着的对象
file 指定文件名
  • 经过 jmap 导出 内存快照,文件命名为 dump.dat
1
2
3
jmap -dump:format=b,file=dump.dat 12498
Dumping heap to /Users/XXX/dump.dat ...
Heap dump file created

导出的 dump 文件能够经过 MATVisualVM 和 jhat 等工具查看分析,后面会详细介绍。

4. jhat堆快照分析工具

jhatJVM Heap Analysis Tool)命令一般与 jmap 搭配使用,用来分析 jmap 生成的 dumpjhat 内置了一个微型的 HTTP/HTML 服务器,生成 dump 的分析结果后,能够在浏览器中查看。

注意:通常不会直接在 服务器 上 进行分析,由于使用 jhat 是一个 耗时 而且 耗费硬件资源 的过程,通常的作法是,把 服务器 生成的 dump 文件复制到 本地 或 其余机器 上进行分析。

命令格式以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Usage:  jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file>

-J<flag> Pass <flag> directly to the runtime system. For
example, -J-mx512m to use a maximum heap size of 512MB
-stack false: Turn off tracking object allocation call stack.
-refs false: Turn off tracking of references to objects
-port <port>: Set the port for the HTTP server. Defaults to 7000
-exclude <file>: Specify a file that lists data members that should
be excluded from the reachableFrom query.
-baseline <file>: Specify a baseline object dump. Objects in
both heap dumps with the same ID and same class will
be marked as not being "new".
-debug <int>: Set debug level.
0: No debug output
1: Debug hprof file parsing
2: Debug hprof file parsing, no server
-version Report version number
-h|-help Print this help and exit
<file> The file to read

参数含义以下:

参数 参数值默认值 参数含义
stack true 关闭 对象分配调用栈跟踪。若是分配位置信息在堆转储中不可用。则必须将此标志设置为false。
refs true 关闭 对象引用跟踪。默认状况下,返回的指针是指向其余特定对象的对象。如 反向连接 或 输入引用,会统计/计算堆中的全部对象
port 7000 设置jhat HTTP server的端口号
exclude 指定对象查询时须要排除的数据成员列表文件
baseline 指定一个 基准堆转储。在两个heap dumps中有相同object ID的对象时,会被标记为不是新的,其余对象被标记为新的。在比较两个不一样的堆转储时颇有用
debug 0 设置debug级别,0表示不输出调试信息。值越大则表示输出更详细的debug信息
version 启动后只显示版本信息就退出
J jhat命令实际上会启动一个JVM来执行,经过-J能够在启动JVM时传入一些 启动参数。例如, -J-Xmx512m则指定运行jhat 的Java虚拟机使用的最大堆内存为512MB。
  • 前面提到,经过 jmap dump 出来的文件能够用 MATVisualVM 等工具查看,这里咱们用 jhat 查看:
1
2
3
4
5
6
7
8
9
10
$ jhat -port 7000 dump.dat
Reading from dump.dat...
Dump file created Sun Aug 12 12:15:02 CST 2018
Snapshot read, resolving...
Resolving 1788693 objects...
Chasing references, expect 357 dots.....................................................................................................................................................................................................................................................................................................................................................................
Eliminating duplicate references.....................................................................................................................................................................................................................................................................................................................................................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
  • 打开浏览器,输入 http://localhost:7000,查看 jhat 的分析报表页面:

  • 能够按照 包名称 查看项目模块中的具体 对象示例

除此以外,报表分析的最后一页,还提供了一些扩展查询:

  • 显示全部的 Root 集合;
  • 显示全部 class 的当前 对象实例数量(包含 JVM 平台相关类);
  • 显示全部 class 的当前 对象实例数量(除去 JVM 平台相关类);
  • 显示 堆内存 中实例对象的 统计直方图(和直接使用 jmap 没有区别);
  • 显示 finalizer 虚拟机 二次回收 的信息摘要;
  • 执行 jhat 提供的 对象查询语言OQL)获取指定对象的实例信息。

注意:jhat 支持根据某些条件来 过滤 或 查询 堆的对象。能够在 jhat 的 html 页面中执行 OQL 语句,来查询符合条件的对象。OQL `具体的语法能够直接访问 http://localhost:7000/oqlhelp。

在具体排查时,须要结合代码,观察是否 大量应该被回收 的对象 一直被引用,或者是否有 占用内存特别大 的对象 没法被回收

5. jstack堆栈跟踪工具

jstack 用于生成 java 虚拟机当前时刻的 线程快照线程快照 是当前 java 虚拟机内 每一条线程 正在执行的 方法堆栈 的 集合。生成线程快照的主要目的是定位线程出现 长时间停顿 的缘由,如 线程间死锁死循环请求外部资源 致使的 长时间等待 等等。

线程出现 停顿 的时候,经过 jstack 来查看 各个线程 的 调用堆栈,就能够知道没有响应的线程到底在后台作什么事情,或者等待什么资源。若是 java 程序 崩溃 生成 core 文件jstack 工具能够经过 core 文件获取 java stack 和 native stack 的信息,从而定位程序崩溃的缘由。

命令格式以下:

1
2
3
4
5
6
7
8
9
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)

参数含义以下:

  • pid:本地 jvm 服务的进程 ID
  • executable core:打印 堆栈跟踪 的核心文件;
  • remote server IP/hostname:远程 debug 服务的 主机名 或 IP 地址;
  • server id:远程 debug 服务的 进程 ID

参数选项说明以下:

参数 参数含义
F 当正常输出请求 不被响应 时,强制输出 线程堆栈
l 除堆栈外,显示关于 锁的附加信息
m 若是调用到 本地方法 的话,能够显示 C/C++ 的堆栈

注意:在实际运行中,每每一次 dump 的信息,还不足以确认问题。建议产生三次 dump 信息,若是每次 dump 都指向同一个问题,才能肯定问题的典型性。

5.1. 系统线程状态

在 dump 文件里,值得关注的 线程状态 有:

  1. 死锁:Deadlock(重点关注)
  2. 执行中:Runnable
  3. 等待资源:Waiting on condition(重点关注)
  4. 等待获取监视器:Waiting on monitor entry(重点关注)
  5. 暂停:Suspended
  6. 对象等待中:Object.wait() 或 TIMED_WAITING
  7. 阻塞:Blocked(重点关注)
  8. 中止:Parked

具体的含义以下所示:

(a). Deadlock

死锁线程,通常指多个线程调用期间发生 资源的相互占用,致使一直等待没法释放的状况。

(b). Runnable

通常指该线程正在 执行状态 中,该线程占用了 资源,正在 处理某个请求。有可能正在传递 SQL 到数据库执行,有可能在对某个文件操做,有可能进行数据类型等转换。

(c). Waiting on condition

该状态在线程等待 某个条件 的发生。具体是什么缘由,能够结合 stacktrace来分析。线程处于这种 等待状态,一旦有数据准备好读以后,线程会从新激活,读取并处理数据。

线程正处于等待资源或等待某个条件的发生,具体的缘由须要结合下面堆栈信息进行分析。

  • 若是 堆栈信息 明确是 应用代码,则证实该线程正在 等待资源。通常是大量 读取某种资源 且该资源采用了 资源锁 的状况下,线程进入 等待状态

  • 若是发现有 大量的线程 都正处于这种状态,而且堆栈信息中得知正在 等待网络读写,这是由于 网络阻塞 致使 线程没法执行,颇有多是一个 网络瓶颈 的征兆:

    • 网络很是 繁忙,几乎消耗了全部的带宽,仍然有大量数据等待网络读写;
    • 网络多是 空闲的,但因为 路由 或 防火墙 等缘由,致使包没法正常到达。
  • 还有一种常见的状况是该线程在 sleep,等待 sleep 的时间到了,将被唤醒。

(d). Locked

线程阻塞,是指当前线程执行过程当中,所须要的资源 长时间等待 却 一直未能获取到,被容器的线程管理器标识为 阻塞状态,能够理解为 等待资源超时 的线程。

(e). Waiting for monitor entry 和 in Object.wait()

Monitor 是 Java 中实现线程之间的 互斥与协做 的主要手段,它能够当作是 对象 或者 Class 的 。每个对象都有一个 monitor

5.1. 死锁示例

下面给出一个 死锁 的案例,在 IntLock 中定义了两个静态的 可重入锁 实例,在主方法中声明了 两个线程 对 两把锁 进行资源竞争。

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
public class DeadLockRunner {
public static void main(String[] args) {
IntLock r1 = new IntLock(1);
IntLock r2 = new IntLock(2);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}

public static class IntLock implements Runnable {
private static ReentrantLock lock1 = new ReentrantLock();
private static ReentrantLock lock2 = new ReentrantLock();
private int lock;

public IntLock(int lock) {
this.lock = lock;
}

@Override
public void run() {
try {
if (lock == 1) {
lock1.lock();

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

lock2.lock();
} else {
lock2.lock();

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

lock1.lock();
}
} finally {
if (lock1.isHeldByCurrentThread()) {
lock1.unlock();
}
if (lock2.isHeldByCurrentThread()) {
lock2.unlock();
}
}
}
}
}

5.2. dump日志分析

启动 DeadLockRunner 的 main() 方法,使用 jps 查看阻塞的 jvm 进程的 id,而后使用 jstack 查看 线程堆栈信息,能够发现两个线程相互 竞争资源,出现死锁

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
$ jstack -l 15584
2018-08-12 20:35:40
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.172-b11 mixed mode):

// 省略...

Found one Java-level deadlock:
=============================
"Thread-1":
waiting for ownable synchronizer 0x000000076ad61180, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "Thread-0"
"Thread-0":
waiting for ownable synchronizer 0x000000076ad611b0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076ad61180> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at io.ostenant.deadlock.DeadLockRunner$IntLock.run(DeadLockRunner.java:47)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076ad611b0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at io.ostenant.deadlock.DeadLockRunner$IntLock.run(DeadLockRunner.java:37)
at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

参考

周志明,深刻理解Java虚拟机:JVM高级特性与最佳实践,机械工业出版社


欢迎关注技术公众号:零壹技术栈

零壹技术栈零壹技术栈

本账号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。

相关文章
相关标签/搜索