前言
通过上一篇的 JVM
垃圾回收知识,我们了解了 JVM
具体的 垃圾回收算法 和几种 垃圾回收器。理论是指导实践的工具,有了理论指导,定位问题的时候,知识和经验是关键基础,数据可以为我们提供依据。
在线上我们经常会遇见如下几个问题:
- 内存泄露;
- 某个进程突然
CPU
飙升; - 线程死锁;
- 响应变慢。
如果遇到了以上这种问题,在 线下环境 可以有各种 可视化的本地工具 支持查看。但是一旦到 线上环境,就没有这么多的 本地调试工具 支持,我们该如何基于 监控工具 来进行定位问题?
我们一般会基于 数据收集 来定位问题,而数据的收集离不开 监控工具 的处理,比如:运行日志、异常堆栈、GC
日志、线程快照、堆内存快照 等。为了解决以上问题,我们常用的 JVM
性能调优监控工具 大致有:jps
、jstat
、jstack
、jmap
、jhat
、hprof
、jinfo
。
正文
如果想要查看 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
名称等信息。
命令格式如下:
1 |
usage: jps [-help] |
参数含义如下:
- -q: 不输出 类名称、
Jar
名称 和传入main
方法的 参数; - -l: 输出
main
类或Jar
的 全限定名称; - -m: 输出传入
main
方法的 参数; - -v: 输出传入
JVM
的参数。
2. jinfo配置信息查看工具
jinfo
(JVM Configuration info
)这个命令作用是实时查看和调整 虚拟机运行参数。之前的 jps -v
命令只能查看到显示 指定的参数,如果想要查看 未显示 的参数的值就要使用 jinfo
命令。
1 |
Usage: |
参数含义如下:
- 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 |
- 查看正在运行的
jvm
进程的所有 参数信息。
1 |
$ jinfo 31983 |
- 查看正在运行的
jvm
进程的 环境变量信息。
1 |
$ jinfo -sysprops 31983 |
2. jstat信息统计监控工具
jstat
是用于识别 虚拟机 各种 运行状态信息 的命令行工具。它可以显示 本地 或者 远程虚拟机 进程中的 类装载、内存、垃圾收集、jit
编译 等运行数据,它是 线上 定位 jvm
性能 的首选工具。
jstat
工具提供如下的 jvm
监控功能:
- 类的加载 及 卸载 的情况;
- 查看 新生代、老生代 及 元空间(
MetaSpace
)的 容量 及使用情况; - 查看 新生代、老生代 及 元空间(
MetaSpace
)的 垃圾回收情况,包括垃圾回收的 次数,垃圾回收所占用的 时间; - 查看 新生代 中
Eden
区及Survior
区中 容量 及 分配情况 等。
命令格式如下:
1 |
Usage: jstat -help|-options |
参数含义如下:
- option: 参数选项。
- -t: 可以在打印的列加上
timestamp
列,用于显示系统运行的时间。 - -h: 可以在 周期性数据 的时候,可以在指定输出多少行以后输出一次 表头。
- -t: 可以在打印的列加上
- 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 |
$ jstat -class 8615 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
Loaded | 已经装载的类的数量 |
Bytes | 装载类所占用的字节数 |
Unloaded | 已经卸载类的数量 |
Bytes | 卸载类的字节数 |
Time | 装载和卸载类所花费的时间 |
2.2. compiler
显示虚拟机 实时编译(JIT
)的 次数 和 耗时 等信息。
1 |
$ jstat -compiler 8615 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
Compiled | 编译任务执行数量 |
Failed | 编译任务执行失败数量 |
Invalid | 编译任务执行失效数量 |
Time | 编译任务消耗时间 |
FailedType | 最后一个编译失败任务的类型 |
FailedMethod | 最后一个编译失败任务所在的类及方法 |
2.3. gc
显示 垃圾回收(gc
)相关的 堆信息,查看 gc
的 次数 及 时间。
1 |
$ jstat -gc 8615 |
比如下面输出的是 GC
信息,采样 时间间隔 为 250ms
,采样数为 4
:
1 |
$ jstat -gc 8615 250 4 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
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 |
$ jstat -gccapacity 8615 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
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 |
$ jstat -gcmetacapacity 8615 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
MCMN | 最小 元数据空间容量 |
MCMX | 最大 元数据空间容量 |
MC | 当前 元数据空间容量 |
CCSMN | 最小压缩 类空间容量 |
CCSMX | 最大压缩 类空间容量 |
CCSC | 当前 压缩类空间容量 |
YGC | 从应用程序启动到采样时 年轻代 中 gc 次数 |
FGC | 从应用程序启动到采样时 老年代 中 gc 次数 |
FGCT | 从应用程序启动到采样时 老年代 gc 所用时间 |
GCT | 从应用程序启动到采样时 gc 用的 总时间 |
2.6. gcnew
显示 年轻代对象 的相关信息,包括两个 survivor
区和 一个 Eden
区。
1 |
$ jstat -gcnew 8615 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
S0C | 年轻代中第一个 survivor 的容量 |
S1C | 年轻代中第二个 survivor 的容量 |
S0U | 年轻代中第一个 survivor 目前已使用空间 |
S1U | 年轻代中第二个 survivor 目前已使用空间 |
TT | 持有次数限制 |
MTT | 最大持有次数限制 |
DSS | 期望的 幸存区 大小 |
EC | 年轻代中 Eden 的容量 |
EU | 年轻代中 Eden 目前已使用空间 |
YGC | 从应用程序启动到采样时 年轻代 中 gc 次数 |
YGCT | 从应用程序启动到采样时 年轻代 中 gc 所用时间 |
2.7. gcnewcapacity
查看 年轻代 对象的信息及其占用量。
1 |
$ jstat -gcnewcapacity 8615 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
NGCMN | 年轻代中初始化(最小)的大小 |
NGCMX | 年轻代的最大容量 |
NGC | 年轻代中当前的容量 |
S0CMX | 年轻代中第一个 survivor 的最大容量 |
S0C | 年轻代中第一个 survivor 的容量 |
S1CMX | 年轻代中第二个 survivor 的最大容量 |
S1C | 年轻代中第二个 survivor 的容量 |
ECMX | 年轻代中 Eden 的最大容量 |
EC | 年轻代中 Eden 的容量 |
YGC | 从应用程序启动到采样时 年轻代 中 gc 次数 |
FGC | 从应用程序启动到采样时 老年代 中 gc 次数 |
2.8. gcold
显示 老年代对象 的相关信息。
1 |
$ jstat -gcold 8615 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
MC | 元空间(metaspace )的容量 |
MU | 元空间(metaspace )目前已使用空间 |
CCSC | 压缩类空间大小 |
CCSU | 压缩类空间 使用 大小 |
OC | 老年代 的容量 |
OU | 老年代 目前已使用空间 |
YGC | 从应用程序启动到采样时 年轻代 中 gc 次数 |
FGC | 从应用程序启动到采样时 老年代 中 gc 次数 |
FGCT | 从应用程序启动到采样时 老年代 gc 所用时间 |
GCT | 从应用程序启动到采样时 gc 用的 总时间 |
2.9. gcoldcapacity
查看 老年代 对象的信息及其占用量。
1 |
$ jstat -gcoldcapacity 8615 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
OGCMN | 老年代 中初始化(最小)的大小 |
OGCMX | 老年代 的最大容量 |
OGC | 老年代 当前新生成的容量 |
OC | 老年代 的容量 |
YGC | 从应用程序启动到采样时 年轻代 中 gc 的次数 |
FGC | 从应用程序启动到采样时 老年代 中 gc 的次数 |
FGCT | 从应用程序启动到采样时 老年代 中 gc 所用时间 |
GCT | 从应用程序启动到采样时 gc 用的 总时间 |
2.10. gcutil
显示 垃圾回收(gc
)过程中的信息,包括各个 内存的使用占比,垃圾回收 时间 和回收 次数。
1 |
$ jstat -gcutil 8615 |
参数列表及含义如下:
参数 | 参数含义 |
---|---|
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 |
Usage: |
参数含义如下:
- 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
等功能。
3.1. heap
显示 堆 中的摘要信息。包括 堆内存 的使用情况,正在使用的 GC
算法、堆配置参数 和 各代中堆内存 使用情况。可以用此来判断内存目前的 使用情况 以及 垃圾回收 情况。
1 |
$ jmap -heap 11368 |
这里主要对 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 |
$ jmap -histo:live 12498 |
其中,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 |
jmap -dump:format=b,file=dump.dat 12498 |
导出的 dump
文件可以通过 MAT
、VisualVM
和 jhat
等工具查看分析,后面会详细介绍。
4. jhat堆快照分析工具
jhat
(JVM Heap Analysis Tool
)命令通常与 jmap
搭配使用,用来分析 jmap
生成的 dump
。jhat
内置了一个微型的 HTTP/HTML
服务器,生成 dump
的分析结果后,可以在浏览器中查看。
注意:一般不会直接在 服务器 上 进行分析,因为使用
jhat
是一个 耗时 并且 耗费硬件资源 的过程,一般的做法是,把 服务器 生成的dump
文件复制到 本地 或 其他机器 上进行分析。
命令格式如下:
1 |
Usage: jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file> |
参数含义如下:
参数 | 参数值默认值 | 参数含义 |
---|---|---|
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 |
- 打开浏览器,输入
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 |
Usage: |
参数含义如下:
- 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
文件里,值得关注的 线程状态 有:
- 死锁:Deadlock(重点关注)
- 执行中:Runnable
- 等待资源:Waiting on condition(重点关注)
- 等待获取监视器:Waiting on monitor entry(重点关注)
- 暂停:Suspended
- 对象等待中:Object.wait() 或 TIMED_WAITING
- 阻塞:Blocked(重点关注)
- 停止: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 |
public class DeadLockRunner { |
5.2. dump日志分析
启动 DeadLockRunner
的 main()
方法,使用 jps
查看阻塞的 jvm
进程的 id
,然后使用 jstack
查看 线程堆栈信息,可以发现两个线程相互 竞争资源,出现死锁。
1 |
$ jstack -l 15584 |
参考
周志明,深入理解Java虚拟机:JVM高级特性与最佳实践,机械工业出版社
欢迎关注技术公众号:零壹技术栈
本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。