jvm-05-监控分析工具

文中案例均基于JDK8

Reference

第三方工具

  • arthas 阿里开源的Java诊断利器
  • perfma 国内jvm在线分析平台
  • GCViewer 图形化gc日志分析工具
  • GCeasy 在线gc日志分析工具

JDK自带工具

jps

官方参考文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html

查看java进程ID,同样可以查看远程服务的java进程

$ jps --help
illegal argument: --help
usage: jps [-help]
       jps [-q] [-mlvV] [<hostid>]

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

# 查看java进程ID
$ jps
19559 hello.jar
2151 world.jar

# 仅显示进程号
$ jps -q
48997
38406

# 查看java进程和main方法接收的参数
$ jps -m
19559 springbootapp.jar --spring.profiles.active=test --server.port=8080

# 查看java进程启动时的jvm参数
$ jps -v
48997 hello.jar -Dfile.encoding=utf8 -Xms1g -Xmx1g -XX:InitialBootClassLoaderMetaspaceSize=64M -XX:MaxMetaspaceSize=256M -XX:+DisableExplicitGC -XX:+UseG1GC -XX:MaxGCPauseMillis=100

# 查看java进程和执行的jar文件或者主类完整路径
$ jps -l
48997 /home/fengjx/demo/app.jar
38406 com.fengjx.app.ServerBootApp

# 不常用,查看通过flag文件传递到jvm中的参数
$ jps -V

jstat

官方参考文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html

jstat -options查看jstat可以查询哪些数据

$ jstat -options
-class              # 显示类装载、卸载数量、总空间和类装载耗时
-compiler           # 显示JIT编译器编译过程统计信息
-gc                 # 显示java堆情况,包括Eden、Survivor、老年代、永久代(元空间)容量和GC合计信息
-gccapacity         # 显示内容和-gc基本相同,主要关注各区域的最大、最小空间
-gcutil             # 显示内容和-gc基本相同,主要关注已使用空间百分比
-gccause            # 与-gcutil相同,但是会额外输出上一次GC产生原因
-gcmetacapacity     # 显示元空间使用的最大、最小空间
-gcnew              # 显示新生代GC状况
-gcnewcapacity      # 显示内容和-gcnew基本相同,主要关注使用到的最大、最小空间
-gcold              # 显示老年代GC状况
-gcoldcapacity      # 显示内容和-gcold基本相同,主要关注使用到的最大、最小空间
-printcompilation   # 显示已经被JIT编译的方法

gcutil: 虚拟机统计信息监控工具

# 15200是进程号,1s表示每隔1秒输出一次,可以指定count,在执行count次数后退出
# jstat -gcutil <pid> <interval> <count>
jstat -gcutil 15200 1s
S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
0.00  66.05  28.80  93.85  24.03  93.78  45401 1731.240    12   22.165 1753.405
0.00  66.05  51.80  93.85  24.03  93.78  45401 1731.240    12   22.165 1753.405
0.00  66.05  62.20  93.85  24.03  93.78  45401 1731.240    12   22.165 1753.405
  • S0: S0当前使用率 - Survivor space 0 utilization as a percentage of the space’s current capacity.
  • S1: S1当前使用率 - Survivor space 1 utilization as a percentage of the space’s current capacity.
  • E: Eden当前使用率 - Eden space utilization as a percentage of the space’s current capacity.
  • O: 老年代当前使用率 - Old space utilization as a percentage of the space’s current capacity.
  • M: 元空间当前使用率 - Metaspace utilization as a percentage of the space’s current capacity.
  • CCS: 压缩类空间使用率 - Compressed class space utilization as a percentage.
  • YGC: Young GC发生次数 - Number of young generation GC events.
  • YGCT: Young GC总耗时 - Young generation garbage collection time.
  • FGC: Full GC发生次数 - Number of full GC events.
  • FGCT: Full GC总耗时 - Full garbage collection time.
  • GCT: GC总耗时 - Total garbage collection time.

jinfo: java配置信息查看工具

官方参考文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jinfo.html

# 格式:jinfo [option] pid

# 打印java进程参数信息,包括系统参数和jvm参数
$ jinfo 19205
Attaching to process ID 19205, 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
java.vm.version = 25.161-b12
sun.boot.library.path = /home/fengjx/app/jdk1.8.0_161/jre/lib/amd64
java.protocol.handler.pkgs = org.springframework.boot.loader
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
...
...

VM Flags:
Non-default VM flags: -XX:CICompilerCount=15 -XX:ConcGCThreads=6 -XX:G1HeapRegionSize=1048576 -XX:GCLogFileSize=10485760 -XX:InitialBootClassLoaderMetaspaceSize=67108864 -XX:InitialHeapSize=1073741824 -XX:MarkStackSize=4194304 -XX:MaxGCPauseMillis=200 -XX:MaxHeapSize=1073741824 -XX:MaxMetaspaceSize=268435456 -XX:MaxNewSize=643825664 -XX:MinHeapDeltaBytes=1048576 -XX:NumberOfGCLogFiles=3 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:+UseGCLogFileRotation
Command line:  -Xms1g -Xmx1g -XX:InitialBootClassLoaderMetaspaceSize=64M -XX:MaxMetaspaceSize=256M -XX:+DisableExplicitGC -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xloggc:/home/fengjx/logs/app/gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=10M -DlogFile.dir=/home/fengjx/logs/app

# 查看是否使用了G1GC
$ jinfo -flag UseG1GC 19205
-XX:+UseG1GC
# 查看期望GC停顿时间
$ jinfo -flag MaxGCPauseMillis 19205
-XX:MaxGCPauseMillis=200

# 在不重启进程的情况下动态调整JVM参数
# jinfo -flag [+|-]<name> $pid 修改bool类型参数
# jinfo -flag <name>=<value> $pid 修改制定参数值
# 动态参数调整有限制,只有下面这些参数才可以动态调整
$ java -XX:+PrintFlagsFinal -version | grep manageable
    intx CMSAbortablePrecleanWaitMillis            = 100                                 {manageable}
    intx CMSTriggerInterval                        = -1                                  {manageable}
    intx CMSWaitDuration                           = 2000                                {manageable}
    bool HeapDumpAfterFullGC                       = false                               {manageable}
    bool HeapDumpBeforeFullGC                      = false                               {manageable}
    bool HeapDumpOnOutOfMemoryError                = false                               {manageable}
    ccstr HeapDumpPath                              =                                     {manageable}
    uintx MaxHeapFreeRatio                          = 100                                 {manageable}
    uintx MinHeapFreeRatio                          = 0                                   {manageable}
    bool PrintClassHistogram                       = false                               {manageable}
    bool PrintClassHistogramAfterFullGC            = false                               {manageable}
    bool PrintClassHistogramBeforeFullGC           = false                               {manageable}
    bool PrintConcurrentLocks                      = false                               {manageable}
    bool PrintGC                                   = false                               {manageable}
    bool PrintGCDateStamps                         = false                               {manageable}
    bool PrintGCDetails                            = false                               {manageable}
    bool PrintGCID                                 = false                               {manageable}
    bool PrintGCTimeStamps                         = false                               {manageable}
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)

# 动态修改打印gc触发时间
$ jinfo -flag +PrintGCDateStamps 48997
# 动态修改java堆dump文件输出路径
jinfo -flag HeapDumpPath=/home/fengjx/app/logs 48997

jmap: java内存快照监控工具

官方参考文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jmap.html

命令格式:jmap [ option ] pid

$ jmap -h
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)

where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system    
选项 作用 说明
-dump 生成java堆快照 格式:-dump:[live,]format=b,file=<filename> $pid
live表示是否只dump出存活对象
eg: jmap -dump:live,format=b,file=dump.bin 1349
-histo 显示堆中对象统计信息:类、实例数量、合计容量 格式:-histo[:live] $pid
live表示是否只dump出存活对象
eg: jmap -histo:live 1349(通常输出的内容太多,可以加 `
-heap 显示java堆详细信息:使用哪种回收器、参数配置、分代情况等 只在Linux / Solaris平台有效
-finalizerinfo 显示在F-Queue中等待Finalizer线程执行finalize方法的对象 只在Linux / Solaris平台有效
-permstat 以ClassLoader为统计口径显示永久代内存状态 只在Linux / Solaris平台有效
-F 当虚拟机进程对 -dump 选项没有响应时,可以使用这个选项强制生成dump快照 只在Linux / Solaris平台有效
  • 输出java进程存活对象快照,快照文件可以用jdk自带的jhat、VisualVM和MAT (Memory Analyzer Tool)进行分析

    $ jmap -dump:live,format=b,file=dump.bin 997
    Dumping heap to /home/app/logs/jvm/dump.bin ...
    Heap dump file created
    
  • 输出各对象内存占用信息top n

    $ jmap -histo:live 997 | head -10
    num     #instances         #bytes  class name
    ----------------------------------------------
    1:         81758       11251848  [C
    2:         80423        1930152  java.lang.String
    3:         44951        1438432  java.util.concurrent.ConcurrentHashMap$Node
    4:         11237        1246448  java.lang.Class
    5:         12979         957472  [Ljava.util.HashMap$Node;
    6:           455         859568  [Ljava.util.concurrent.ConcurrentHashMap$Node;
    7:         21068         842720  java.util.LinkedHashMap$Entry
    
    • instances表示实例数
    • bytes表示占用内存大小(字节)
    • class name表示对象类名

      • [C is a char[]
      • [S is a short[]
      • [I is a int[]
      • [B is a byte[]
      • [[I is a int[][]

      [C对象一般跟String有关,String内部使用final char[]数组来保存数据

jhat: 虚拟机堆转储快照分析工具

官方参考文档:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jhat.html

jhat (JVM Heap Analysis Tool)命令与jmap配合使用,来分析jmap生成的堆快照文件。jhat内置内置了一个微型http服务,可以直接在浏览器中查看dump文件分析结果。

实际工作中很少使用jhat来分析dump文件。通常是将dump文件传到其他机器,或者开发机,通过其他工具(例如:VisualVM和MAT)进行分析。

执行jhat <filename>后,直接访问:http://localhost:7000 就能看到分析结果

$ jhat dump.bin
Reading from dump.bin...
Dump file created Wed Nov 20 22:32:37 CST 2019
Snapshot read, resolving...
Resolving 570509 objects...
Chasing references, expect 114 dots..................................................................................................................
Eliminating duplicate references..................................................................................................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

jstack: java堆栈跟踪工具

jstack (Stack Trace For Java) 命令可以生产虚拟机当前时刻的线程快照(threaddump),用来定位线程出现长时间停顿的原因。例如:线程死锁、死循环、请求外部资源(例如:IO请求)等。

命令格式:jstack [option] pid

$ jstack -h
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)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message
选项 作用
-F 当正常输出的请求不被响应时,强制输出线程堆栈
-l 出堆栈外,显示关于锁的附加信息
-m 如果调用到本地方法的话,可以显示C/C++的堆栈
$ jstack -l 31994
2019-11-20 23:32:12
Full thread dump OpenJDK 64-Bit Server VM (11-internal+125 mixed mode):

Threads class SMR info:
_java_thread_list=0x00007fd3087b6430, length=16, elements={
0x00007fd309015000, 0x00007fd30a018800, 0x00007fd30a01b800, 0x00007fd30a01d800,
0x00007fd30885f000, 0x00007fd30a00f800, 0x00007fd309035000, 0x00007fd30a068800,
0x00007fd3088ec000, 0x00007fd3088e4800, 0x00007fd309042000, 0x00007fd3091c4800,
0x00007fd3098b4000, 0x00007fd309209000, 0x00007fd30920a000, 0x00007fd30a1ba800
}

"main" #1 prio=5 os_prio=31 cpu=847.59ms elapsed=4363.85s tid=0x00007fd309015000 nid=0x1703 in Object.wait()  [0x0000700004f9c000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait(java.base@11-internal/Native Method)
	- waiting on <no object reference available>
	at com.intellij.execution.rmi.RemoteServer.start(RemoteServer.java:89)
	- waiting to re-lock in wait() <0x00000007d0174fd8> (a java.lang.Object)
	at org.jetbrains.idea.maven.server.RemoteMavenServer36.main(RemoteMavenServer36.java:23)

   Locked ownable synchronizers:
	- None

通常输出的内容会比较多,可以将内容输出到文件中,通过其他工具来分析( 例如: perfma,一个国内jvm在线分析平台

jstack -l 31994 > app-31994.jstack

线程状态:

  • Deadlock: 死锁
  • Waiting on condition: 等待资源
  • Waiting on monitor entry: 等待获取监视器
  • Blocked: 阻塞
  • Runnable: 执行中
  • Suspended: 暂停
  • TIMED_WAITING: 对象等待中
  • Parked: 停止