转载:《JVM系列(七) - JVM线上监控工具》
官网:《Java Platform, Standard Edition Tools Reference》

jps(Java Process Status)

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

jps 可以列出 jvm 进程 lvmid,主类类名,main 函数参数, jvm 参数,jar 名称等信息。

命令格式如下:

  1. usage: jps [-help]
  2. jps [-q] [-mlvV] [<hostid>]
  3. Definitions:
  4. <hostid>: <hostname>[:<port>]

参数含义如下:

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

jinfo

jinfo(JVM Configuration info)这个命令作用是实时查看调整虚拟机运行参数。之前的 jps -v 命令只能查看到显示指定的参数,如果想要查看未显示的参数的值就要使用 jinfo 命令。

  1. Usage:
  2. jinfo [option] <pid>
  3. (to connect to running process)
  4. jinfo [option] <executable <core>
  5. (to connect to a core file)
  6. jinfo [option] [server_id@]<remote server IP or hostname>
  7. (to connect to remote debug server)
  8. where <option> is one of:
  9. -flag <name> to print the value of the named VM flag
  10. -flag [+|-]<name> to enable or disable the named VM flag
  11. -flag <name>=<value> to set the named VM flag to the given value
  12. -flags to print VM flags
  13. -sysprops to print Java system properties
  14. <no option> to print both of the above
  15. -h | -help to print this help message

参数含义如下:

  • 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. $ jinfo -flags 31983
  2. Attaching to process ID 31983, please wait
  3. Debugger attached successfully.
  4. Server compiler detected.
  5. JVM version is 25.91-b14
  6. 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
  7. 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

jstat

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

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

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

命令格式如下:

  1. Usage: jstat -help|-options
  2. 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 编译的方法信息;


class

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

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

参数列表及含义如下:

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

compiler

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

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

参数列表及含义如下:

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

gc

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

  1. $ jstat -gc 8615
  2. S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
  3. 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. $ jstat -gc 8615 250 4
  2. S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
  3. 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
  4. 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
  5. 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
  6. 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. $ jstat -gccapacity 8615
  2. NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
  3. 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 次数

gcmetacapacity

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

  1. $ jstat -gcmetacapacity 8615
  2. MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT
  3. 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. $ jstat -gcnew 8615
  2. S0C S1C S0U S1U TTv MTT DSS EC EU YGC YGCT
  3. 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. $ jstat -gcnewcapacity 8615
  2. NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
  3. 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. $ jstat -gcold 8615
  2. MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
  3. 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. $ jstat -gcoldcapacity 8615
  2. OGCMN OGCMX OGC OC YGC FGC FGCT GCT
  3. 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. $ jstat -gcutil 8615
  2. S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
  3. 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 用的总时间

jmap

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

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

命令格式如下:

  1. Usage:
  2. jmap [option] <pid>
  3. (to connect to running process)
  4. jmap [option] <executable <core>
  5. (to connect to a core file)
  6. jmap [option] [server_id@]<remote server IP or hostname>
  7. (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. $ jmap -heap 11368
  2. Attaching to process ID 11368, please wait...
  3. Debugger attached successfully.
  4. Server compiler detected.
  5. JVM version is 25.101-b13
  6. using thread-local object allocation.
  7. Parallel GC with 2 thread(s)
  8. Heap Configuration:
  9. MinHeapFreeRatio = 0
  10. MaxHeapFreeRatio = 100
  11. MaxHeapSize = 2684354560 (2560.0MB)
  12. NewSize = 1073741824 (1024.0MB)
  13. MaxNewSize = 1073741824 (1024.0MB)
  14. OldSize = 1610612736 (1536.0MB)
  15. NewRatio = 2
  16. SurvivorRatio = 8
  17. MetaspaceSize = 21807104 (20.796875MB)
  18. CompressedClassSpaceSize = 1073741824 (1024.0MB)
  19. MaxMetaspaceSize = 17592186044415 MB
  20. G1HeapRegionSize = 0 (0.0MB)
  21. Heap Usage:
  22. PS Young Generation
  23. Eden Space:
  24. capacity = 852492288 (813.0MB)
  25. used = 420427144 (400.95056915283203MB)
  26. free = 432065144 (412.04943084716797MB)
  27. 49.31741317993014% used
  28. From Space:
  29. capacity = 113770496 (108.5MB)
  30. used = 2299712 (2.19317626953125MB)
  31. free = 111470784 (106.30682373046875MB)
  32. 2.021360617079493% used
  33. To Space:
  34. capacity = 107479040 (102.5MB)
  35. used = 0 (0.0MB)
  36. free = 107479040 (102.5MB)
  37. 0.0% used
  38. PS Old Generation
  39. capacity = 1610612736 (1536.0MB)
  40. used = 50883368 (48.526161193847656MB)
  41. free = 1559729368 (1487.4738388061523MB)
  42. 3.1592552860577903% used
  43. 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. $ jmap -histo:live 12498
  2. num #instances #bytes class name
  3. ----------------------------------------------
  4. 1: 50358 7890344 [C
  5. 2: 22887 2014056 java.lang.reflect.Method
  6. 3: 3151 1485512 [B
  7. 4: 49267 1182408 java.lang.String
  8. 5: 7836 871384 java.lang.Class
  9. 6: 24149 772768 java.util.concurrent.ConcurrentHashMap$Node
  10. 7: 20785 482256 [Ljava.lang.Class;
  11. 8: 8357 435248 [Ljava.lang.Object;
  12. 9: 10035 401400 java.util.LinkedHashMap$Entry
  13. 10: 4803 369488 [Ljava.util.HashMap$Node;
  14. 11: 10763 344416 java.util.HashMap$Node
  15. 12: 5205 291480 java.util.LinkedHashMap
  16. 13: 3055 219960 java.lang.reflect.Field
  17. 14: 120 193408 [Ljava.util.concurrent.ConcurrentHashMap$Node;
  18. 15: 11224 179584 java.lang.Object
  19. 16: 1988 146152 [Ljava.lang.reflect.Method;
  20. 17: 3036 145728 org.aspectj.weaver.reflect.ShadowMatchImpl
  21. 18: 1771 141680 java.lang.reflect.Constructor
  22. 19: 4903 117672 org.springframework.core.MethodClassKey
  23. 20: 3263 104416 java.lang.ref.WeakReference
  24. 21: 2507 100280 java.lang.ref.SoftReference
  25. 22: 2523 97600 [I
  26. 23: 3036 97152 org.aspectj.weaver.patterns.ExposedState
  27. 24: 2072 95280 [Ljava.lang.String;
  28. 25: 954 91584 org.springframework.beans.GenericTypeAwarePropertyDescriptor
  29. 26: 1633 91448 java.lang.Class$ReflectionData
  30. 27: 3142 90520 [Z
  31. 28: 1671 80208 java.util.HashMap
  32. 29: 3244 77856 java.util.ArrayList
  33. 30: 3037 72880 [Lorg.aspectj.weaver.ast.Var;
  34. 31: 1809 72360 java.util.WeakHashMap$Entry
  35. 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

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

  1. jmap -dump:live,format=b,file=heap.bin <pid>

参数含义如下:

参数 参数含义
dump 堆到文件
format 指定输出格式
live 指明是活着的对象
file 指定文件名

通过 jmap 导出内存快照,文件命名为 dump.bin:

  1. [root@localhost data]# jmap -dump:format=b,file=dump.bin 12498
  2. Dumping heap to /data/dump.bin ...
  3. Heap dump file created

导出的 dump 文件可以通过 MAT、VisualVM 和 jhat 等工具查看分析,后面会详细介绍。

jhat

jhat(JVM Heap Analysis Tool)命令通常与 jmap 搭配使用,用来分析 jmap 生成的 dump。jhat 内置了一个微型的 HTTP/HTML 服务器,生成 dump 的分析结果后,可以在浏览器中查看。

一般不用,更多是用图形化界面工具分析 dump 文件。

注意:一般不会直接在服务器上进行分析,因为使用 jhat 是一个耗时并且耗费硬件资源的过程,一般的做法是,把服务器生成的 dump 文件复制到本地或其他机器上进行分析。

命令格式如下:

  1. Usage: jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file>
  2. -J<flag> Pass <flag> directly to the runtime system. For
  3. example, -J-mx512m to use a maximum heap size of 512MB
  4. -stack false: Turn off tracking object allocation call stack.
  5. -refs false: Turn off tracking of references to objects
  6. -port <port>: Set the port for the HTTP server. Defaults to 7000
  7. -exclude <file>: Specify a file that lists data members that should
  8. be excluded from the reachableFrom query.
  9. -baseline <file>: Specify a baseline object dump. Objects in
  10. both heap dumps with the same ID and same class will
  11. be marked as not being "new".
  12. -debug <int>: Set debug level.
  13. 0: No debug output
  14. 1: Debug hprof file parsing
  15. 2: Debug hprof file parsing, no server
  16. -version Report version number
  17. -h|-help Print this help and exit
  18. <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. $ jhat -port 7000 dump.dat
  2. Reading from dump.dat...
  3. Dump file created Sun Aug 12 12:15:02 CST 2018
  4. Snapshot read, resolving...
  5. Resolving 1788693 objects...
  6. Chasing references, expect 357 dots.....................................................................................................................................................................................................................................................................................................................................................................
  7. Eliminating duplicate references.....................................................................................................................................................................................................................................................................................................................................................................
  8. Snapshot resolved.
  9. Started HTTP server on port 7000
  10. Server is ready.

打开浏览器,输入 http://localhost:7000,查看 jhat 的分析报表页面:

1.5-JVM 命令行调优工具 - 图1

可以按照包名称查看项目模块中的具体对象示例:

1.5-JVM 命令行调优工具 - 图2

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

1.5-JVM 命令行调优工具 - 图3

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

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

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

jstack

jstack 用于生成 java 虚拟机当前时刻的线程快照。线程快照是当前 java 虚拟机内每一条线程正在执行的方法堆栈的集合。生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等等。
线程出现停顿的时候,通过 jstack 来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。如果 java 程序崩溃生成 core 文件,jstack 工具可以通过 core 文件获取 java stack 和 native stack 的信息,从而定位程序崩溃的原因。

命令格式如下:

  1. Usage:
  2. jstack [-l] <pid>
  3. (to connect to running process)
  4. jstack -F [-m] [-l] <pid>
  5. (to connect to a hung process)
  6. jstack [-m] [-l] <executable> <core>
  7. (to connect to a core file)
  8. jstack [-m] [-l] [server_id@]<remote server IP or hostname>
  9. (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 文件里,值得关注的线程状态有:

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

具体的含义如下所示:

Deadlock

死锁线程,一般指多个线程调用期间发生资源的相互占用,导致一直等待无法释放的情况。

Runnable

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

Waiting on condition

该状态在线程等待某个条件的发生。具体是什么原因,可以结合 stacktrace来分析。线程处于这种等待状态,一旦有数据准备好读之后,线程会重新激活,读取并处理数据。
线程正处于等待资源或等待某个条件的发生,具体的原因需要结合下面堆栈信息进行分析。

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

Blocked

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

Waiting for monitor entry 和 in Object.wait()

Monitor 是 Java 中实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class 的锁。每一个对象都有一个 monitor。

死锁示例

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

  1. public class DeadLockRunner {
  2. public static void main(String[] args) {
  3. IntLock r1 = new IntLock(1);
  4. IntLock r2 = new IntLock(2);
  5. Thread t1 = new Thread(r1);
  6. Thread t2 = new Thread(r2);
  7. t1.start();
  8. t2.start();
  9. }
  10. public static class IntLock implements Runnable {
  11. private static ReentrantLock lock1 = new ReentrantLock();
  12. private static ReentrantLock lock2 = new ReentrantLock();
  13. private int lock;
  14. public IntLock(int lock) {
  15. this.lock = lock;
  16. }
  17. @Override
  18. public void run() {
  19. try {
  20. if (lock == 1) {
  21. lock1.lock();
  22. try {
  23. Thread.sleep(500);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. lock2.lock();
  28. } else {
  29. lock2.lock();
  30. try {
  31. Thread.sleep(500);
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. lock1.lock();
  36. }
  37. } finally {
  38. if (lock1.isHeldByCurrentThread()) {
  39. lock1.unlock();
  40. }
  41. if (lock2.isHeldByCurrentThread()) {
  42. lock2.unlock();
  43. }
  44. }
  45. }
  46. }
  47. }

dump 日志分析

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

  1. $ jstack -l 15584
  2. 2018-08-12 20:35:40
  3. Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.172-b11 mixed mode):
  4. // 省略...
  5. Found one Java-level deadlock:
  6. =============================
  7. "Thread-1":
  8. waiting for ownable synchronizer 0x000000076ad61180, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  9. which is held by "Thread-0"
  10. "Thread-0":
  11. waiting for ownable synchronizer 0x000000076ad611b0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  12. which is held by "Thread-1"
  13. Java stack information for the threads listed above:
  14. ===================================================
  15. "Thread-1":
  16. at sun.misc.Unsafe.park(Native Method)
  17. - parking to wait for <0x000000076ad61180> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
  18. at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
  19. at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
  20. at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
  21. at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
  22. at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
  23. at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
  24. at io.ostenant.deadlock.DeadLockRunner$IntLock.run(DeadLockRunner.java:47)
  25. at java.lang.Thread.run(Thread.java:748)
  26. "Thread-0":
  27. at sun.misc.Unsafe.park(Native Method)
  28. - parking to wait for <0x000000076ad611b0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
  29. at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
  30. at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
  31. at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
  32. at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
  33. at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
  34. at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
  35. at io.ostenant.deadlock.DeadLockRunner$IntLock.run(DeadLockRunner.java:37)
  36. at java.lang.Thread.run(Thread.java:748)
  37. Found 1 deadlock.

作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/ugtgq0 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

mm_reward_qrcode_1574408256457.png