学习JVM(4)——JVM常用工具和命令

前面详细介绍了有关JVM原理性的知识,这一篇介绍一些在日常工作学习中我们能用到的工具和命令,使用jvm自带的命令可以方便地在生产监控和打印堆栈的日志信息中帮我们定位问题,当然也有很多成熟的工具,比如:jconsole、大名鼎鼎的VisualVM、IBM的Memory Analyzer等等。所有的工具几乎都是依赖于jdk的接口和底层的相关命令。

1. 常用命令

我们一般会基于数据收集来定位问题,而数据的收集离不开监控工具的处理,比如:运行日志、异常堆栈、GC日志、线程快照、堆内存快照等。常用的性能调优监控工具包括:jps、jstat、jmap、jhat、jstack、jinfo、hprof等,下面一一介绍。

1. jps

JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。jps是用于查看有权访问的hotspot虚拟机的进程。当未指定hostid时,默认查看本机jvm进程,否则查看指定的hostid机器上的jvm进程,此时hostid所指机器必须开启jstatd服务。jps可以列出jvm进程lvmid,主类类名,main函数参数,jvm参数,jar名称等信息。
命令格式:

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

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

参数:

  • -l 输出主类全名或jar路径的全限定名称
  • -q 不输出类名称、jar名称和传入main方法的参数,只输出LVMID
  • -m 输出JVM启动时传递给main()的参数
  • -v 输出JVM启动时显示指定的JVM参数

其中[option]、[hostid]参数也可以不写。
实例:

1
2
3
4
$ jps -l -m
28920 org.apache.catalina.startup.Bootstrap start
11589 org.apache.catalina.startup.Bootstrap start
25816 sun.tools.jps.Jps -l -m

2. jstat

jstat(JVM statistics Monitoring)是用于识别虚拟机各种运行状态的命令行工具。它可以显示本地或者远程虚拟机进程中的类加载、内存、垃圾收集、jit编译等运行数据,它是线上定位jvm性能的的首选工具。
jstat工具提供如下的jvm监控功能:

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

命令格式:

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

参数:

  • option: 参数选项

    -t: 可以在打印的列上加上timestamp列,用于显示系统运行的时间<br />      -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 编译 的方法信息;

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 装载和卸载类所花费的时间

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 最后一个编译失败任务所在的类及方法

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 用的 总时间

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 次数

gcmetacapaciity: 显示元空间(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 用的 总时间

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 所用时间

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 次数

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 用的 总时间

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 用的 总时间

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 用的 总时间

gccause: 垃圾收集器统计概述(同gcutil),附加最近两次垃圾回收事件的原因。
示例:

1
2
3
$ jstat -gccause 28920
S0 S1 E O P YGC YGCT FGC FGCT GCT LGCC GCC
12.45 0.00 33.85 0.00 4.44 4 0.242 0 0.000 0.242 Allocation Failure No GC

参数列表及含义如下:

参数 参数含义
S0 年轻代中 第一个 survivor已使用 的占当前容量百分比
S1 年轻代中 第二个 survivor已使用 的占当前容量百分比
E 年轻代中 Eden已使用 的占当前容量百分比
O 老年代已使用 的占当前容量百分比
P perm代已使用的占当前容量百分比
YGC 从应用程序启动到采样时 年轻代gc 次数
YGCT 从应用程序启动到采样时 年轻代gc 所用时间
FGC 从应用程序启动到采样时 老年代 gc 次数
FGCT 从应用程序启动到采样时 老年代 gc 所用时间
GCT 从应用程序启动到采样时 gc 用的 总时间
LGCC 最近垃圾回收的原因
GCC 当前垃圾回收的原因

printcompilation: hotspot编译方法统计
示例:

1
2
3
$ jstat -printcompilation 28920
Compiled Size Type Method
1291 78 1 java/util/ArrayList indexOf

参数列表及含义如下:

参数 参数含义
Compiled 被执行的编译任务的数量
Size 方法字节码的字节数
Type 编译类型
Method 编译方法的类名和方法名。类名使用”/“代替”.”作为空间分隔符。方法名是给出类的方法名。格式是一致于HotSpot -XX:+PrintComplation选项

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内存快照分析基本上包含了histo、clstats、finalizerinfo等功能。

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

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+类名 其他对象

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

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文件可以通过MAT、VisualVM和jhat等工具查看分析。

4. jhat

jhat(JVM Heap Analysis Tool)命令通常与jmap搭配使用,用来分析jmap生成的dump。jhat内置了一个微型的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出来的文件可以用MAT、VisualVM等工具查看,这里我们用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 支持根据某些条件来 过滤查询 堆的对象。可以在 jhathtml 页面中执行 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 都指向同一个问题,才能确定问题的典型性。

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

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

具体的含义如下所示:
Deadlock(死锁线程): 一般指多个线程调用期间发生 资源的相互占用,导致一直等待无法释放的情况。
Runnable: 一般指该线程正在 执行状态 中,该线程占用了 资源,正在 处理某个请求。有可能正在传递 SQL 到数据库执行,有可能在对某个文件操作,有可能进行数据类型等转换。
Waiting on condition: 该状态在线程等待 某个条件 的发生。具体是什么原因,可以结合 stacktrace 来分析。线程处于这种 等待状态,一旦有数据准备好读之后,线程会重新激活,读取并处理数据。
线程正处于等待资源或等待某个条件的发生,具体的原因需要结合下面堆栈信息进行分析。

  • 如果 堆栈信息 明确是 应用代码,则证明该线程正在 等待资源。一般是大量 读取某种资源 且该资源采用了 资源锁 的情况下,线程进入 等待状态
  • 如果发现有 大量的线程 都正处于这种状态,并且堆栈信息中得知正在 等待网络读写,这是因为 网络阻塞 导致 线程无法执行,很有可能是一个 网络瓶颈 的征兆:
    • 网络非常 繁忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写;
    • 网络可能是 空闲的,但由于 路由防火墙 等原因,导致包无法正常到达。
  • 还有一种常见的情况是该线程在 sleep,等待 sleep 的时间到了,将被唤醒。

Locked(线程阻塞): 是指当前线程执行过程中,所需要的资源 长时间等待一直未能获取到,被容器的线程管理器标识为 阻塞状态,可以理解为 等待资源超时 的线程。
Waiting for monitor entry 和 in Object.wait(): MonitorJava 中实现线程之间的 互斥与协作 的主要手段,它可以看成是 对象 或者 Class。每一个对象都有一个 monitor

死锁示例:
下面给出一个死锁的示例,在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();
}
}
}
}
}

dump日志分析:
启动 DeadLockRunnermain() 方法,使用 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.

6. jinfo

jinfo(JVM 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. 常用工具

工具作为图形化界面来展示更能直观的发现问题,另一方面一些耗费性能的分析(dump文件分析)一般也不会在生产环境直接分析,往往dump下来的文件达1G左右,人工分析效率较低,因此利用工具来分析jvm相关问题,往往可以事半功倍。
jvm监控工具一般分为两类:一种是jdk自带的工具,一种是第三方的分析工具。jdk自带工具一般在jdk/bin目录下,以.exe的形式直接点击使用,其中包括的分析工具已经很强大,几乎涉及了方方面面,但最常用的只有两款:jconsole.exe和jvisualvm.exe;第三方的分析工具很多,各自的侧重点不同,比较有代表性的:MAT(Memory Analyzer Tool)、GChisto等。
对于大型 JAVA 应用程序来说,再精细的测试也难以堵住所有的漏洞,即便我们在测试阶段进行了大量卓有成效的工作,很多问题还是会在生产环境下暴露出来,并且很难在测试环境中进行重现。JVM 能够记录下问题发生时系统的部分运行状态,并将其存储在堆转储 (Heap Dump) 文件中,从而为我们分析和诊断问题提供了重要的依据。其中VisualVM和MAT是dump文件的分析利器。

1. jconsole

Jconsole(Java Monitoring and Management Console)是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控,是一个基于JMX(java management extensions)的GUI性能监测工具。jconsole使用jvm的扩展机制获取并展示虚拟机中运行的应用程序的性能和资源消耗等信息。
直接在jdk/bin目录下点击jconsole.exe即可启动,界面如下:

在弹出的框中可以选择本机的监控本机的java应用,也可以选择远程的java服务来监控,如果监控远程服务需要在tomcat启动脚本中添加如下代码:

-Dcom.sun.management.jmxremote.port=6969
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

连接进去之后,就可以看到jconsole概览图和主要的功能:概述、内存、线程、类、VM、MBeans

  • 概述,以图表的方式显示出堆内存使用量,活动线程数,已加载的类,CUP占用率的折线图,可以非常清晰的观察在程序执行过程中的变动情况。

  • 内存,主要展示了内存的使用情况,同时可以查看堆和非堆内存的变化值对比,也可以点击执行GC来处罚GC的执行

  • 线程,主界面展示线程数的活动数和峰值,同时点击左下方线程可以查看线程的详细信息,比如线程的状态是什么,堆栈内容等,同时也可以点击“检测死锁”来检查线程之间是否有死锁的情况。

  • 类,主要展示已加载类的相关信息。
  • VM 概要,展示JVM所有信息总览,包括基本信息、线程相关、堆相关、操作系统、VM参数等。
  • Mbean,查看Mbean的属性,方法等。

2. VisualVM

VisualVM 是一个工具,它提供了一个可视界面,用于查看 Java 虚拟机 (Java Virtual Machine, JVM) 上运行的基于 Java 技术的应用程序(Java 应用程序)的详细信息。VisualVM 对 Java Development Kit (JDK) 工具所检索的 JVM 软件相关数据进行组织,并通过一种使您可以快速查看有关多个 Java 应用程序的数据的方式提供该信息。您可以查看本地应用程序以及远程主机上运行的应用程序的相关数据。此外,还可以捕获有关 JVM 软件实例的数据,并将该数据保存到本地系统,以供后期查看或与其他用户共享。
VisualVM 是javajdk自带的最牛逼的调优工具了吧,也是我平时使用最多调优工具,几乎涉及了jvm调优的方方面面。同样是在jdk/bin目录下面双击jvisualvm.exe既可使用,启动起来后和jconsole 一样同样可以选择本地和远程,如果需要监控远程同样需要配置相关参数,主界面如下;

VisualVM可以根据需要安装不同的插件,每个插件的关注点都不同,有的主要监控GC,有的主要监控内存,有的监控线程等。

如何安装:

1、从主菜单中选择“工具”>“插件”。
2、在“可用插件”标签中,选中该插件的“安装”复选框。单击“安装”。
3、逐步完成插件安装程序。

我这里以 Eclipse(pid 22296)为例,双击后直接展开,主界面展示了系统和jvm两大块内容,点击右下方jvm参数和系统属性可以参考详细的参数信息.

因为VisualVM的插件太多,我这里主要介绍三个我主要使用几个:监控、线程、Visual GC
监控的主页其实也就是,cpu、内存、类、线程的图表

线程和jconsole功能没有太大的区别

Visual GC 是常常使用的一个功能,可以明显的看到年轻代、老年代的内存变化,以及gc频率、gc的时间等。

以上的功能其实jconsole几乎也有,VisualVM更全面更直观一些,另外VisualVM非常多的其它功能,可以分析dump的内存快照,dump出来的线程快照并且进行分析等,还有其它很多的插件大家可以去探索。

3. MAT

MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。
通常内存泄露分析被认为是一件很有难度的工作,一般由团队中的资深人士进行。不过要介绍的 MAT(Eclipse Memory Analyzer)被认为是一个“傻瓜式“的堆转储文件分析工具,你只需要轻轻点击一下鼠标就可以生成一个专业的分析报告。和其他内存泄露分析工具相比,MAT 的使用非常容易,基本可以实现一键到位,即使是新手也能够很快上手使用。
MAT以eclipse 插件的形式来安装,具体的安装过程就不在描述了,可以利用visualvm或者是 jmap命令生产堆文件,导入eclipse mat中生成分析报告:

生产这会报表的同时也会在dump文件的同级目录下生成三份(dump_Top_Consumers.zip、dump_Leak_Suspects.zip、dump_Top_Components.zip)分析结果的html文件,方便发送给相关同事来查看。
需要关注的是下面的Actions、Reports、Step by Step区域:

  • Histogram:列出内存中的对象,对象的个数以及大小,支持正则表达式查找,也可以计算出该类所有对象的retained size

  • Dominator Tree:列出最大的对象以及其依赖存活的Object (大小是以Retained Heap为标准排序的)

  • Top Consumers : 通过图形列出最大的object

  • duplicate classes :检测由多个类装载器加载的类
  • Leak Suspects :内存泄漏分析

  • Top Components: 列出大于总堆数的百分之1的报表。

  • Component Report:分析对象属于同一个包或者被同一个类加载器加载

以上只是一个初级的介绍,mat还有更强大的使用,比如对比堆内存,在生产环境中往往为了定位问题,每隔几分钟dump出一下内存快照,随后在对比不同时间的堆内存的变化来发现问题。

###

4. GChisto

GChisto是一款专业分析gc日志的工具,可以通过gc日志来分析:Minor GC、Full GC的时间、频率等,通过列表、报表、图表等不同的形式来反映GC的情况。
配置好本地的jdk环境之后,双击GChisto.jar,在弹出的输入框中点击add选择gc.log日志。

GC Pause Stats: 可以查看GC的次数、GC的时间、GC的开销、最大GC时间和最小GC时间等,以及相应的柱状图

GC Pause Distribution: 查看GC停顿的详细分布,x轴表示垃圾收集停顿时间,y轴表示的是停顿次数
GC Timeline: 显示整个时间线上的垃圾收集。

注意:这款工具已经不再维护。

5. GC Easy

这是一个web工具,在线使用非常方便。
网址:http://gceasy.io
进入官网,将打包好的zip或者gz为后缀的压缩包上传,过一会儿就会拿到分析结果。


推荐使用此工具进行GC分析。

6. gcviewer

GCViewer也是一款分析小工具,用于可视化查看由Sun / Oracle, IBM, HP 和 BEA Java 虚拟机产生的垃圾收集器的日志,gcviewer个人感觉显示 的界面比较乱没有GChisto更专业一些。

参考文档:

  1. jvm系列(四):jvm调优-命令篇
  2. jvm系列(七):jvm调优-工具篇
  3. JVM系列(七) - JVM线上监控工具