经过上一篇的 JVM
垃圾回收知识,咱们了解了 JVM
具体的 垃圾回收算法 和几种 垃圾回收器。理论是指导实践的工具,有了理论指导,定位问题的时候,知识和经验是关键基础,数据能够为咱们提供依据。html
在线上咱们常常会碰见以下几个问题:java
CPU
飙升;若是遇到了以上这种问题,在 线下环境 能够有各类 可视化的本地工具 支持查看。可是一旦到 线上环境,就没有这么多的 本地调试工具 支持,咱们该如何基于 监控工具 来进行定位问题?算法
咱们通常会基于 数据收集 来定位问题,而数据的收集离不开 监控工具 的处理,好比:运行日志、异常堆栈、GC
日志、线程快照、堆内存快照 等。为了解决以上问题,咱们经常使用的 JVM
性能调优监控工具 大体有:jps
、jstat
、jstack
、jmap
、jhat
、hprof
、jinfo
。spring
若是想要查看 Java
进程中 线程堆栈 的信息,能够选择 jstack
命令。若是要查看 堆内存,可使用 jmap
导出并使用 jhat
来进行分析,包括查看 类的加载信息,GC
算法,对象 的使用状况等。可使用 jstat
来对 JVM
进行 统计监测,包括查看各个 区内存 和 GC
的状况,还可使用 hprof
查看 CPU
使用率,统计 堆内存 使用状况。下面会详细的介绍这几个工具的用法。数据库
jps
是用于查看有权访问的 hotspot
虚拟机 的进程。当未指定 hostid
时,默认查看 本机 jvm
进程,不然查看指定的 hostid
机器上的 jvm
进程,此时 hostid
所指机器必须开启 jstatd
服务。编程
jps
能够列出 jvm
进程 lvmid
,主类类名,main
函数参数, jvm
参数,jar
名称等信息。后端
命令格式以下:api
usage: jps [-help]
jps [-q] [-mlvV] [<hostid>]
Definitions:
<hostid>: <hostname>[:<port>]
复制代码
参数含义以下:数组
Jar
名称 和传入 main
方法的 参数;main
类或 Jar
的 全限定名称;main
方法的 参数;JVM
的参数。jinfo
(JVM Configuration info
)这个命令做用是实时查看和调整 虚拟机运行参数。以前的 jps -v
命令只能查看到显示 指定的参数,若是想要查看 未显示 的参数的值就要使用 jinfo
命令。浏览器
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)
复制代码
参数含义以下:
jvm
服务的进程 ID
;debug
服务的 主机名 或 IP
地址;debug
服务的 进程 ID
。参数选项说明以下:
参数 | 参数含义 |
---|---|
flag | 输出指定 args 参数的值 |
flags | 不须要 args 参数,输出全部 JVM 参数的值 |
sysprops | 输出系统属性,等同于 System.getProperties() |
jvm
进程的 扩展参数。$ 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
进程的全部 参数信息。$ 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
进程的 环境变量信息。$ 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 =
复制代码
jstat
是用于识别 虚拟机 各类 运行状态信息 的命令行工具。它能够显示 本地 或者 远程虚拟机 进程中的 类装载、内存、垃圾收集、jit
编译 等运行数据,它是 线上 定位 jvm
性能 的首选工具。
jstat
工具提供以下的 jvm
监控功能:
MetaSpace
)的 容量 及使用状况;MetaSpace
)的 垃圾回收状况,包括垃圾回收的 次数,垃圾回收所占用的 时间;Eden
区及 Survior
区中 容量 及 分配状况 等。命令格式以下:
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
复制代码
参数含义以下:
timestamp
列,用于显示系统运行的时间。pid
)。参数选项说明以下:
ClassLoad
的相关信息;JIT
编译 的相关信息;gc
相关的 堆信息;metaspace
的大小;-gcutil
),同时显示 最后一次 或 当前 正在发生的垃圾回收的 诱因;JIT
编译 的方法信息;显示和监视 类装载、卸载数量、总空间 以及 耗费的时间。
$ jstat -class 8615
Loaded Bytes Unloaded Bytes Time
7271 13325.8 1 0.9 2.98
复制代码
参数列表及含义以下:
参数 | 参数含义 |
---|---|
Loaded | 已经装载的类的数量 |
Bytes | 装载类所占用的字节数 |
Unloaded | 已经卸载类的数量 |
Bytes | 卸载类的字节数 |
Time | 装载和卸载类所花费的时间 |
显示虚拟机 实时编译(JIT
)的 次数 和 耗时 等信息。
$ jstat -compiler 8615
Compiled Failed Invalid Time FailedType FailedMethod
3886 0 0 1.29 0
复制代码
参数列表及含义以下:
参数 | 参数含义 |
---|---|
Compiled | 编译任务执行数量 |
Failed | 编译任务执行失败数量 |
Invalid | 编译任务执行失效数量 |
Time | 编译任务消耗时间 |
FailedType | 最后一个编译失败任务的类型 |
FailedMethod | 最后一个编译失败任务所在的类及方法 |
显示 垃圾回收(gc
)相关的 堆信息,查看 gc
的 次数 及 时间。
$ 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
:
$ 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 用的 总时间 |
显示 虚拟机内存 中三代 年轻代(young
),老年代(old
),元空间(metaspace
)对象的使用和占用大小。
$ 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 次数 |
显示 元空间(metaspace
)中 对象 的信息及其占用量。
$ 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 用的 总时间 |
显示 年轻代对象 的相关信息,包括两个 survivor
区和 一个 Eden
区。
$ 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 所用时间 |
查看 年轻代 对象的信息及其占用量。
$ 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 次数 |
显示 老年代对象 的相关信息。
$ 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 用的 总时间 |
查看 老年代 对象的信息及其占用量。
$ 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 用的 总时间 |
显示 垃圾回收(gc
)过程当中的信息,包括各个 内存的使用占比,垃圾回收 时间 和回收 次数。
$ 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 用的 总时间 |
jmap
(JVM Memory Map
) 命令用来查看 堆内存 使用情况,通常结合 jhat
使用,用于生成 heap dump
文件。jmap
不只能生成 dump
文件,还能够查询 finalize
执行队列、Java
堆 和 元空间 metaspace
的详细信息,如当前 使用率、当前使用的是哪一种 收集器 等等。
若是不使用这个命令,还可使用
-XX:+HeapDumpOnOutOfMemoryError
参数来让虚拟机出现OOM
的时候,自动生成dump
文件。
命令格式以下:
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)
复制代码
参数含义以下:
jvm
服务的进程 ID
;debug
服务的 主机名 或 IP
地址;debug
服务的 进程 ID
。参数选项说明以下:
参数 | 参数含义 |
---|---|
heap | 显示 堆 中的摘要信息 |
histo | 显示 堆 中对象的统计信息 |
histo[:live] | 只显示 堆 中 存活对象 的统计信息 |
clstats | 显示 类加载 的统计信息 |
finalizerinfo | 显示在 F-Queue 队列 等待 Finalizer 线程执行 finalizer 方法的对象 |
dump | 导出内存转储快照 |
注意:
dump
内存快照分析基本上包含了histo
、clstats
、finalizerinfo
等功能。
显示 堆 中的摘要信息。包括 堆内存 的使用状况,正在使用的 GC
算法、堆配置参数 和 各代中堆内存 使用状况。能够用此来判断内存目前的 使用状况 以及 垃圾回收 状况。
$ 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:live
前会进行 full gc
,若是带上 live
则只统计 活对象。不加 live
的堆大小要大于加 live
堆的大小。
$ 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
分析查看。jmap
进行 dump
的命令格式以下:
jmap -dump:format=b,file=dumpFileName
复制代码
参数含义以下:
参数 | 参数含义 |
---|---|
dump | 堆到文件 |
format | 指定输出格式 |
live | 指明是活着的对象 |
file | 指定文件名 |
jmap
导出 内存快照,文件命名为 dump.dat
:jmap -dump:format=b,file=dump.dat 12498
Dumping heap to /Users/XXX/dump.dat ...
Heap dump file created
复制代码
导出的 dump
文件能够经过 MAT
、VisualVM
和 jhat
等工具查看分析,后面会详细介绍。
jhat
(JVM Heap Analysis Tool
)命令一般与 jmap
搭配使用,用来分析 jmap
生成的 dump
。jhat
内置了一个微型的 HTTP/HTML
服务器,生成 dump
的分析结果后,能够在浏览器中查看。
注意:通常不会直接在 服务器 上 进行分析,由于使用
jhat
是一个 耗时 而且 耗费硬件资源 的过程,通常的作法是,把 服务器 生成的dump
文件复制到 本地 或 其余机器 上进行分析。
命令格式以下:
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
查看:$ 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。
在具体排查时,须要结合代码,观察是否 大量应该被回收 的对象 一直被引用,或者是否有 占用内存特别大 的对象 没法被回收。
jstack
用于生成 java
虚拟机当前时刻的 线程快照。线程快照 是当前 java
虚拟机内 每一条线程 正在执行的 方法堆栈 的 集合。生成线程快照的主要目的是定位线程出现 长时间停顿 的缘由,如 线程间死锁、死循环、请求外部资源 致使的 长时间等待 等等。
线程出现 停顿 的时候,经过 jstack
来查看 各个线程 的 调用堆栈,就能够知道没有响应的线程到底在后台作什么事情,或者等待什么资源。若是 java
程序 崩溃 生成 core
文件,jstack
工具能够经过 core
文件获取 java stack
和 native stack
的信息,从而定位程序崩溃的缘由。
命令格式以下:
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)
复制代码
参数含义以下:
jvm
服务的进程 ID
;debug
服务的 主机名 或 IP
地址;debug
服务的 进程 ID
。参数选项说明以下:
参数 | 参数含义 |
---|---|
F | 当正常输出请求 不被响应 时,强制输出 线程堆栈 |
l | 除堆栈外,显示关于 锁的附加信息 |
m | 若是调用到 本地方法 的话,能够显示 C/C++ 的堆栈 |
注意:在实际运行中,每每一次
dump
的信息,还不足以确认问题。建议产生三次dump
信息,若是每次dump
都指向同一个问题,才能肯定问题的典型性。
在 dump
文件里,值得关注的 线程状态 有:
具体的含义以下所示:
(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
。
下面给出一个 死锁 的案例,在 IntLock
中定义了两个静态的 可重入锁 实例,在主方法中声明了 两个线程 对 两把锁 进行资源竞争。
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();
}
}
}
}
}
复制代码
启动 DeadLockRunner
的 main()
方法,使用 jps
查看阻塞的 jvm
进程的 id
,而后使用 jstack
查看 线程堆栈信息,能够发现两个线程相互 竞争资源,出现死锁。
$ 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高级特性与最佳实践,机械工业出版社
欢迎关注技术公众号:零壹技术栈
本账号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。