JVM线上监控工具

    技术2025-07-09  13

    文章目录

    简介JVM常见监控工具 & 指令jps进程监控工具jinfo配置信息查看工具jstat信息统计监控工具classcompilergcgccapacitygcmetacapacitygcnewgcnewcapacitygcoldgcoldcapacitygcutil jmap堆内存统计工具heaphistodump jhat堆快照分析工具jstack堆栈跟踪工具系统线程状态死锁示例dump日志分析

    简介

    在线上我们经常会遇见如下几个问题:

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

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

    我们一般会基于 数据收集 来定位问题,而数据的收集离不开 监控工具 的处理,比如:运行日志、异常堆栈、GC 日志、线程快照、堆内存快照 等。为了解决以上问题,我们常用的 JVM 性能调优监控工具 大致有:jps、jstat、jstack、jmap、jhat、hprof、jinfo。

    如果想要查看 Java 进程中 线程堆栈 的信息,可以选择 jstack 命令。

    JVM常见监控工具 & 指令

    如果要查看 堆内存,可以使用 jmap 导出并使用 jhat 来进行分析,包括查看 类的加载信息,GC 算法,对象 的使用情况等。

    可以使用 jstat 来对 JVM 进行 统计监测,包括查看各个 区内存 和 GC 的情况。

    可以使用 hprof 查看 CPU 使用率,统计 堆内存 使用情况。

    下面会详细的介绍这几个工具的用法。

    jps进程监控工具

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

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

    命令格式如下:

    usage: jps [-help] jps [-q] [-mlvV] [<hostid>] Definitions: <hostid>: <hostname>[:<port>]

    参数含义如下:

    -q: 不输出 类名称、Jar 名称 和传入 main 方法的 参数;-l: 输出 main 类或 Jar 的 全限定名称;-m: 输出传入 main 方法的 参数;-v: 输出传入 JVM 的参数。 [root@iz2ze9pdcwzj9htp1df8mwz ~]# jps -lm | head -n 3 26081 alpha-app-proxy-server.jar 3042 alpha-mall-server.jar 18437 com.install4j.runtime.launcher.UnixLauncher start 9d17dc87 org.sonatype.nexus.karaf.NexusMain [root@iz2ze9pdcwzj9htp1df8mwz ~]# jps -v | head -n 3 26081 jar -Xms128m -Xmx128m -Dspring.profiles.active=dev 3042 jar -Xms128m -Xmx128m -Dspring.profiles.active=dev 18437 UnixLauncher -Dinstall4j.jvmDir=/opt/modules/jdk1.8.0_161 ... [root@iz2ze9pdcwzj9htp1df8mwz ~]# jps -q | head -n 3 26081 3042 18437

    jinfo配置信息查看工具

    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)

    参数含义如下:

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

    参数选项说明如下:

    参数参数含义flag输出指定 args 参数的值flags不需要 args 参数,输出所有 JVM 参数的值sysprops输出系统属性,等同于 System.getProperties() 查看正在运行的 jvm 进程的 扩展参数。 [root@test2 ~]# jinfo -flags 19173 Attaching to process ID 19173, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.161-b12 Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=125829120 -XX:MaxHeapSize=2013265920 -XX:MaxNewSize=671088640 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=41943040 -XX:OldSize=83886080 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC Command line: -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 查看正在运行的 jvm 进程的所有 参数信息。 [root@test2 ~]# jinfo 19173 Attaching to process ID 19173, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.161-b12 Java System Properties: java.runtime.name = Java(TM) SE Runtime Environment jna.platform.library.path = /usr/lib64:/lib64:/usr/lib:/lib:/usr/lib64/mysql java.vm.version = 25.161-b12 sun.boot.library.path = /opt/modules/jdk1.8.0_161/jre/lib/amd64 mail.smtp.sendpartial = true java.vendor.url = http://java.oracle.com/ java.vm.vendor = Oracle Corporation path.separator = : guice.disable.misplaced.annotation.check = true file.encoding.pkg = sun.io java.vm.name = Java HotSpot(TM) 64-Bit Server VM jna.loaded = true sun.os.patch.level = unknown sun.java.launcher = SUN_STANDARD user.country = US user.dir = /opt/modules/jenkins java.vm.specification.name = Java Virtual Machine Specification java.runtime.version = 1.8.0_161-b12 java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment os.arch = amd64 java.endorsed.dirs = /opt/modules/jdk1.8.0_161/jre/lib/endorsed line.separator = java.io.tmpdir = /tmp java.vm.specification.vendor = Oracle Corporation os.name = Linux mail.smtps.sendpartial = true sun.jnu.encoding = UTF-8 svnkit.http.methods = Digest,Basic,NTLM,Negotiate jnidispatch.path = /root/.cache/JNA/temp/jna674036302077776065.tmp jetty.git.hash = a304fd9f351f337e7c0e2a7c28878dd536149c6c 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 = 3.10.0-514.26.2.el7.x86_64 user.home = /root user.timezone = Asia/Shanghai java.awt.printerjob = sun.print.PSPrinterJob file.encoding = UTF-8 java.specification.version = 1.8 user.name = root java.class.path = jenkins.war java.vm.specification.version = 1.8 sun.arch.data.model = 64 svnkit.ssh2.persistent = false sun.java.command = jenkins.war java.home = /opt/modules/jdk1.8.0_161/jre user.language = en java.specification.vendor = Oracle Corporation awt.toolkit = sun.awt.X11.XToolkit java.vm.info = mixed mode java.version = 1.8.0_161 java.ext.dirs = /opt/modules/jdk1.8.0_161/jre/lib/ext:/usr/java/packages/lib/ext sun.boot.class.path = /opt/modules/jdk1.8.0_161/jre/lib/resources.jar:/opt/modules/jdk1.8.0_161/jre/lib/rt.jar:/opt/modules/jdk1.8.0_161/jre/lib/sunrsasign.jar:/opt/modules/jdk1.8.0_161/jre/lib/jsse.jar:/opt/modules/jdk1.8.0_161/jre/lib/jce.jar:/opt/modules/jdk1.8.0_161/jre/lib/charsets.jar:/opt/modules/jdk1.8.0_161/jre/lib/jfr.jar:/opt/modules/jdk1.8.0_161/jre/classes java.awt.headless = true java.vendor = Oracle Corporation file.separator = / java.vendor.url.bug = http://bugreport.sun.com/bugreport/ sun.io.unicode.encoding = UnicodeLittle sun.font.fontmanager = sun.awt.X11FontManager sun.cpu.endian = little executable-war = /opt/modules/jenkins/jenkins.war sun.cpu.isalist = VM Flags: Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=125829120 -XX:MaxHeapSize=2013265920 -XX:MaxNewSize=671088640 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=41943040 -XX:OldSize=83886080 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC Command line: -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 查看正在运行的 jvm 进程的 环境变量信息。 [root@test2 ~]# jinfo -sysprops 19173 Attaching to process ID 19173, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.161-b12 java.runtime.name = Java(TM) SE Runtime Environment jna.platform.library.path = /usr/lib64:/lib64:/usr/lib:/lib:/usr/lib64/mysql java.vm.version = 25.161-b12 sun.boot.library.path = /opt/modules/jdk1.8.0_161/jre/lib/amd64 mail.smtp.sendpartial = true java.vendor.url = http://java.oracle.com/ java.vm.vendor = Oracle Corporation path.separator = : guice.disable.misplaced.annotation.check = true file.encoding.pkg = sun.io java.vm.name = Java HotSpot(TM) 64-Bit Server VM jna.loaded = true sun.os.patch.level = unknown sun.java.launcher = SUN_STANDARD user.country = US user.dir = /opt/modules/jenkins java.vm.specification.name = Java Virtual Machine Specification java.runtime.version = 1.8.0_161-b12 java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment os.arch = amd64 java.endorsed.dirs = /opt/modules/jdk1.8.0_161/jre/lib/endorsed line.separator = java.io.tmpdir = /tmp java.vm.specification.vendor = Oracle Corporation os.name = Linux mail.smtps.sendpartial = true sun.jnu.encoding = UTF-8 svnkit.http.methods = Digest,Basic,NTLM,Negotiate jnidispatch.path = /root/.cache/JNA/temp/jna674036302077776065.tmp jetty.git.hash = a304fd9f351f337e7c0e2a7c28878dd536149c6c 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 = 3.10.0-514.26.2.el7.x86_64 user.home = /root user.timezone = Asia/Shanghai java.awt.printerjob = sun.print.PSPrinterJob file.encoding = UTF-8 java.specification.version = 1.8 user.name = root java.class.path = jenkins.war java.vm.specification.version = 1.8 sun.arch.data.model = 64 svnkit.ssh2.persistent = false sun.java.command = jenkins.war java.home = /opt/modules/jdk1.8.0_161/jre user.language = en java.specification.vendor = Oracle Corporation awt.toolkit = sun.awt.X11.XToolkit java.vm.info = mixed mode java.version = 1.8.0_161 java.ext.dirs = /opt/modules/jdk1.8.0_161/jre/lib/ext:/usr/java/packages/lib/ext sun.boot.class.path = /opt/modules/jdk1.8.0_161/jre/lib/resources.jar:/opt/modules/jdk1.8.0_161/jre/lib/rt.jar:/opt/modules/jdk1.8.0_161/jre/lib/sunrsasign.jar:/opt/modules/jdk1.8.0_161/jre/lib/jsse.jar:/opt/modules/jdk1.8.0_161/jre/lib/jce.jar:/opt/modules/jdk1.8.0_161/jre/lib/charsets.jar:/opt/modules/jdk1.8.0_161/jre/lib/jfr.jar:/opt/modules/jdk1.8.0_161/jre/classes java.awt.headless = true java.vendor = Oracle Corporation file.separator = / java.vendor.url.bug = http://bugreport.sun.com/bugreport/ sun.io.unicode.encoding = UnicodeLittle sun.font.fontmanager = sun.awt.X11FontManager sun.cpu.endian = little executable-war = /opt/modules/jenkins/jenkins.war sun.cpu.isalist =

    jstat信息统计监控工具

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

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

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

    命令格式如下:

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

    参数含义如下:

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

    参数选项说明如下:

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

    class

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

    [root@iz2ze9pdcwzj9htp1df8mwz ~]# jstat -class 17399 Loaded Bytes Unloaded Bytes Time 15053 28183.1 149 210.3 31.71

    参数列表及含义如下:

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

    compiler

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

    [root@iz2ze9pdcwzj9htp1df8mwz ~]# jstat -compiler 17399 Compiled Failed Invalid Time FailedType FailedMethod 9887 5 0 87.76 1 com/mysql/jdbc/AbandonedConnectionCleanupThread run

    参数列表及含义如下:

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

    gc

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

    [root@iz2ze9pdcwzj9htp1df8mwz ~]# jstat -gc 17399 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 1024.0 1024.0 0.0 128.0 41472.0 34525.4 87552.0 86817.5 85120.0 81322.4 10624.0 9987.3 289 1.557 110 24.452 26.009

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

    [root@iz2ze9pdcwzj9htp1df8mwz ~]# jstat -gc 17399 250 4 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 1024.0 1024.0 0.0 128.0 41472.0 34583.1 87552.0 86817.5 85120.0 81322.4 10624.0 9987.3 289 1.557 110 24.452 26.009 1024.0 1024.0 0.0 128.0 41472.0 34583.1 87552.0 86817.5 85120.0 81322.4 10624.0 9987.3 289 1.557 110 24.452 26.009 1024.0 1024.0 0.0 128.0 41472.0 34583.1 87552.0 86817.5 85120.0 81322.4 10624.0 9987.3 289 1.557 110 24.452 26.009 1024.0 1024.0 0.0 128.0 41472.0 34583.1 87552.0 86817.5 85120.0 81322.4 10624.0 9987.3 289 1.557 110 24.452 26.009

    参数列表及含义如下:

    参数参数含义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)对象的使用和占用大小。

    [root@iz2ze9pdcwzj9htp1df8mwz ~]# jstat -gccapacity 17399 NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC 43520.0 43520.0 43520.0 1024.0 1024.0 41472.0 87552.0 87552.0 87552.0 87552.0 0.0 1124352.0 85120.0 0.0 1048576.0 10624.0 289 110

    参数列表及含义如下:

    参数参数含义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)中 对象 的信息及其占用量。

    [root@iz2ze9pdcwzj9htp1df8mwz ~]# jstat -gcmetacapacity 17399 MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT 0.0 1124352.0 85120.0 0.0 1048576.0 10624.0 289 110 24.452 26.009

    参数列表及含义如下:

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

    gcnew

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

    [root@iz2ze9pdcwzj9htp1df8mwz ~]# jstat -gcnew 17399 S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT 1024.0 1024.0 0.0 128.0 15 15 1024.0 41472.0 35391.3 289 1.557

    参数列表及含义如下:

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

    gcnewcapacity

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

    [root@iz2ze9pdcwzj9htp1df8mwz ~]# jstat -gcnewcapacity 17399 NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC 43520.0 43520.0 43520.0 14336.0 1024.0 14336.0 1024.0 42496.0 41472.0 289 110

    参数列表及含义如下:

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

    gcold

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

    $ 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

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

    $ 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)过程中的信息,包括各个 内存的使用占比,垃圾回收 时间 和回收 次数。

    $ 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堆内存统计工具

    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)

    参数含义如下:

    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 算法、堆配置参数 和 各代中堆内存 使用情况。可以用此来判断内存目前的 使用情况 以及 垃圾回收 情况。

    $ 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:MinHeapFreeRatioJVM堆最小空闲比率(default 40)MaxHeapFreeRatio-XX:MaxHeapFreeRatioJVM堆最大空闲比率(default 70)MaxHeapSizeXX:XmxJVM堆的最大大小NewSize-XX:NewSizeJVM堆新生代的默认(初始化)大小MaxNewSize-XX:MaxNewSizeJVM堆新生代的最大大小OldSize-XX:OldSizeJVM堆老年代的默认(初始化)大小NewRatio-XX:NewRatioJVM堆新生代和老年代的大小比例SurvivorRatio-XX:SurvivorRatioJVM堆年轻代中Eden区与Survivor区的大小比值MetaspaceSize-XX:MetaspaceSizeJVM元空间(metaspace)初始化大小MaxMetaspaceSize-XX:MaxMetaspaceSizeJVM元空间(metaspace)最大大小CompressedClass SpaceSize-XX:CompressedClass SpaceSizeJVM类指针压缩空间大小, 默认为1GG1HeapRegionSize-XX:G1HeapRegionSize使用G1垃圾回收器时单个Region的大小,取值为1M至32M

    histo

    打印堆的 对象统计,包括 对象实例数、内存大小 等等。因为在 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 是 对象类型,对象 缩写类型 与 真实类型 的对应说明如下:

    对象缩写类型对象真实类型BbyteCcharDdoubleFfloatIintJlongZboolean[数组,如[I表示int[][L+类名其他对象

    dump

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

    jmap -dump:format=b,file=dumpFileName

    参数含义如下:

    参数参数含义dump堆到文件format指定输出格式live指明是活着的对象file指定文件名 通过 jmap 导出 内存快照,文件命名为 dump.dat: [root@iz2ze9pdcwzj9htp1df8mwz ~]# jmap -dump:format=b,file=dump.dat 17399 Dumping heap to /root/dump.dat ... Heap dump file created

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

    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

    参数含义如下:

    参数参数值默认值参数含义stacktrue关闭 对象分配调用栈跟踪。如果分配位置信息在堆转储中不可用。则必须将此标志设置为false。refstrue关闭 对象引用跟踪。默认情况下,返回的指针是指向其他特定对象的对象。如 反向链接 或 输入引用,会统计/计算堆中的所有对象port7000设置jhat HTTP server的端口号exclude—指定对象查询时需要排除的数据成员列表文件baseline—指定一个 基准堆转储。在两个heap dumps中有相同object ID的对象时,会被标记为不是新的,其他对象被标记为新的。在比较两个不同的堆转储时很有用debug0设置debug级别,0表示不输出调试信息。值越大则表示输出更详细的debug信息version—启动后只显示版本信息就退出J—jhat命令实际上会启动一个JVM来执行,通过-J可以在启动JVM时传入一些 启动参数。例如, -J-Xmx512m则指定运行jhat 的Java虚拟机使用的最大堆内存为512MB。 前面提到,通过 jmap dump 出来的文件可以用 MAT、VisualVM 等工具查看,这里我们用 jhat 查看: $ jhat -port 5001 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:5001/oqlhelp。

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

    jstack堆栈跟踪工具

    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)

    参数含义如下:

    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

    具体的含义如下所示:

    (a). Deadlock

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

    (b). Runnable

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

    ©. 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(); } } } } }

    dump日志分析

    启动 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.

    原文

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

    Processed: 0.013, SQL: 9