Java的JVM问题排查问题

介绍Java的关于排查JVM的命令

Posted by qin4zhang on August 31, 2019

注意

想法及时记录,实现可以待做。

JVM问题排查

线上环境要排查JVM的问题,可以通过自带的命令行查看信息,方便定位问题所在。

jcmd

发送指定的命令请求给JVM,从而获取信息。

jcmd官方文档

命令格式:

jcmd [-l|-h|-help]

jcmd pid|main-class PerfCounter.print

jcmd pid|main-class -f filename

jcmd pid|main-class command[ arguments]

通过-h参数,查看帮助信息:

root:/# jcmd -h
Usage: jcmd <pid | main class> <command ...|PerfCounter.print|-f file>
   or: jcmd -l
   or: jcmd -h

  command must be a valid jcmd command for the selected jvm.
  Use the command "help" to see which commands are available.
  If the pid is 0, commands will be sent to all Java processes.
  The main class argument will be used to match (either partially
  or fully) the class used to start Java.
  If no options are given, lists Java processes (same as -p).

  PerfCounter.print display the counters exposed by this process
  -f  read and execute commands from the file
  -l  list JVM processes on the local machine
  -h  this help

查看进程列表:

root:/# jcmd -l
1 app.jar
25039 sun.tools.jcmd.JCmd -l

查看更多的帮助信息:

root:/# jcmd 1 help
1:
The following commands are available:
VM.native_memory
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
VM.classloader_stats
GC.rotate_log
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.finalizer_info
GC.heap_info
GC.run_finalization
GC.run
VM.uptime
VM.dynlibs
VM.flags
VM.system_properties
VM.command_line
VM.version
help

For more information about a specific command use 'help <command>'.

这里可以看到,通过jcmd可以看到很多GC和JVM的信息,有助于排查问题。

jcmd用法示例

  1. dump堆转储文件
root:/# jcmd 1 help GC.heap_dump
1:
GC.heap_dump
Generate a HPROF format dump of the Java heap.

Impact: High: Depends on Java heap size and content. Request a full GC unless the '-all' option is specified.

Permission: java.lang.management.ManagementPermission(monitor)

Syntax : GC.heap_dump [options] <filename>

Arguments:
        filename :  Name of the dump file (STRING, no default value)

Options: (options must be specified using the <key> or <key>=<value> syntax)
        -all : [optional] Dump all objects, including unreachable objects (BOOLEAN, false)
root:/# jcmd 1 GC.heap_dump filename.hprof
  1. 堆信息
root:/app# jcmd 1 GC.heap_info
1:
 PSYoungGen      total 152576K, used 51763K [0x00000000f6a00000, 0x0000000100000000, 0x0000000100000000)
  eden space 151552K, 34% used [0x00000000f6a00000,0x00000000f9c8cde8,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 153600K, used 50398K [0x00000000ed400000, 0x00000000f6a00000, 0x00000000f6a00000)
  object space 153600K, 32% used [0x00000000ed400000,0x00000000f0537b68,0x00000000f6a00000)
 Metaspace       used 103304K, capacity 108732K, committed 109568K, reserved 1144832K
  class space    used 12478K, capacity 13341K, committed 13568K, reserved 1048576K

jps

jps(JVM Process Status Tool) (opens new window)是虚拟机进程状态工具。它可以显示指定系统内所有的 HotSpot 虚拟机进程状态信息。jps 通过 RMI 协议查询开启了 RMI 服务的远程虚拟机进程状态。

jps官方文档

命令格式:

jps [options] [hostid]
jps [-help]

常用的参数:

  • options: 选项参数
    • m: 输出JVM的启动主类main的参数
    • l: 输出主类的全名
    • v: 显示船体给JVM的参数
    • q: 仅输出本地JVM的进程ID
    • V: 仅输出本地的JVM标识符
  • hostid: 如果不指定,默认为当前主机

jps用法示例

$ jps
18027 Java2Demo.JAR
18032 jps
18005 jstat
$ jps -q
8841
1292
5398
$ jps -l remote.domain
3002 /opt/jdk1.7.0/demo/jfc/Java2D/Java2Demo.JAR
2857 sun.tools.jstatd.jstatd

jstat

jstat(JVM statistics Monitoring) (opens new window),是虚拟机统计信息监视工具。jstat 用于监视虚拟机运行时状态信息,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据。

jstat官方文档

命令格式:

jstat [ generalOption | outputOptions vmid [ interval[s|ms] [ count ] ]

常用参数:

  • option: 选项参数,用于指定用户需要查询的虚拟机信息
    • class: 监视类装载、卸载数量、总空间以及类装载所耗费的时间
    • compiler: 显示 JIT 编译的相关信息
    • gc: 监视 Java 堆状况,包括 Eden 区、两个 survivor 区、老年代、永久代等区的容量、已用空间、GC 时间合计等信息。
    • gccapacity: 显示各个代的容量以及使用情况
    • gccause: 显示垃圾回收的相关信息(通 -gcutil),同时显示最后一次或当前正在发生的垃圾回收的诱因
    • gcnew: 显示新生代信息
    • gcnewcapacity: 显示有关新生代大小及其相应空间的统计信息
    • gcold: 显示老年代和永久代的信息
    • gcoldcapacity: 显示老年代的大小
    • gcmetacapacity: 显示 Metaspace 的大小
    • gcutil: 显示垃圾回收统计信息
    • printcompilation: 显示Java HotSpot VM编译方法统计信息。
  • vmid: 进程ID

jstat用法示例

jstat -class pid

输出参数:

  • Loaded: 加载 class 的数量
  • Bytes: 所占用空间大小
  • Unloaded: 未加载数量
  • Bytes: 未加载占用空间
  • Time: 时间
$ jstat -class 7129
Loaded  Bytes  Unloaded  Bytes     Time
 26749 50405.3      873  1216.8      19.75

jstat -compiler pid

输出参数:

  • Compiled: 编译数量
  • Failed: 失败数量
  • Invalid: 不可用数量
  • Time: 时间
  • FailedType: 失败类型
  • FailedMethod: 失败的方法
$ jstat -compiler 7129
Compiled Failed Invalid   Time   FailedType FailedMethod
   42030      2       0   302.53          1 org/apache/felix/framework/BundleWiringImpl$BundleClassLoader findClass

jstat -gc pid time

输出参数:

  • S0C:年轻代中 To Survivor 的容量(单位 KB);
  • S1C:年轻代中 From Survivor 的容量(单位 KB);
  • S0U:年轻代中 To Survivor 目前已使用空间(单位 KB);
  • S1U:年轻代中 From Survivor 目前已使用空间(单位 KB);
  • EC:年轻代中 Eden 的容量(单位 KB);
  • EU:年轻代中 Eden 目前已使用空间(单位 KB);
  • OC:Old 代的容量(单位 KB);
  • OU:Old 代目前已使用空间(单位 KB);
  • MC:Metaspace 的容量(单位 KB);
  • MU:Metaspace 目前已使用空间(单位 KB);
  • YGC:从应用程序启动到采样时年轻代中 gc 次数;
  • YGCT:从应用程序启动到采样时年轻代中 gc 所用时间 (s);
  • FGC:从应用程序启动到采样时 old 代(全 gc)gc 次数;
  • FGCT:从应用程序启动到采样时 old 代(全 gc)gc 所用时间 (s);
  • GCT:从应用程序启动到采样时 gc 用的总时间 (s)。
$ jstat -gcutil 21891 250 7
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
  0.00  97.02  70.31  66.80  95.52  89.14      7    0.300     0    0.000    0.300
  0.00  97.02  86.23  66.80  95.52  89.14      7    0.300     0    0.000    0.300
  0.00  97.02  96.53  66.80  95.52  89.14      7    0.300     0    0.000    0.300
 91.03   0.00   1.98  68.19  95.89  91.24      8    0.378     0    0.000    0.378
 91.03   0.00  15.82  68.19  95.89  91.24      8    0.378     0    0.000    0.378
 91.03   0.00  17.80  68.19  95.89  91.24      8    0.378     0    0.000    0.378
 91.03   0.00  17.80  68.19  95.89  91.24      8    0.378     0    0.000    0.378
$ jstat -gc 25196 1s 4
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
20928.0 20928.0  0.0    0.0   167936.0  8880.5   838912.0   80291.2   106668.0 100032.1 12772.0 11602.2    760   14.332  580   656.218  670.550
20928.0 20928.0  0.0    0.0   167936.0  8880.5   838912.0   80291.2   106668.0 100032.1 12772.0 11602.2    760   14.332  580   656.218  670.550
20928.0 20928.0  0.0    0.0   167936.0  8880.5   838912.0   80291.2   106668.0 100032.1 12772.0 11602.2    760   14.332  580   656.218  670.550
20928.0 20928.0  0.0    0.0   167936.0  8880.5   838912.0   80291.2   106668.0 100032.1 12772.0 11602.2    760   14.332  580   656.218  670.550

jmap

jmap(JVM Memory Map) (opens new window)是 Java 内存映像工具。jmap 用于生成堆转储快照(一般称为 heapdump 或 dump 文件)。jmap 不仅能生成 dump 文件,还可以查询 finalize 执行队列、Java 堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。

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

jmap官方文档

命令格式:

jmap [options] pid

常用参数:

  • options: 选项参数
    • dump:[live,] format=b, file=filename: 生成堆转储快照。-dump:live 只保存堆中的存活对象。
    • finalizerinfo: 显示在 F-Queue 队列等待执行 finalizer 方法的对象
    • heap: 显示 Java 堆详细信息
    • histo[:live]: 显示堆中对象的统计信息,包括类、实例数量、合计容量。-histo:live 只统计堆中的存活对象。
    • clstats: 打印Java堆的类加载器的统计信息。对于每个类加载器,将打印其名称,激活程度,地址,父类加载器以及已加载的类的数量和大小。
    • F: 强制。当pid不响应时,将此选项与jmap -dump或jmap -histo选项一起使用。在此模式下不支持live子选项。
  • pid: 进程ID

jmap示例

生成 heapdump 快照,堆转储文件,可以用MAT打开进行堆的内存泄漏的分析。

$ jmap -dump:live,format=b,file=dump.hprof 28920
Dumping heap to /home/xxx/dump.hprof ...
Heap dump file created

查看实例数最多的类

$ jmap -histo 29527 | head -n 6

 num     #instances         #bytes  class name
----------------------------------------------
   1:      13673280     1438961864  [C
   2:       1207166      411277184  [I
   3:       7382322      347307096  [Ljava.lang.Object;

查看指定进程的堆信息

注意:使用 CMS GC 情况下,jmap -heap PID 的执行有可能会导致 java 进程挂起。

$ jmap -heap 12379
Attaching to process ID 12379, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 17.0-b16

using thread-local object allocation.
Parallel GC with 6 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 83886080 (80.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 20971520 (20.0MB)
   MaxPermSize      = 88080384 (84.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 9306112 (8.875MB)
   used     = 5375360 (5.1263427734375MB)
   free     = 3930752 (3.7486572265625MB)
   57.761608714788736% used
From Space:
   capacity = 9306112 (8.875MB)
   used     = 3425240 (3.2665634155273438MB)
   free     = 5880872 (5.608436584472656MB)
   36.80634834397007% used
To Space:
   capacity = 9306112 (8.875MB)
   used     = 0 (0.0MB)
   free     = 9306112 (8.875MB)
   0.0% used
PS Old Generation
   capacity = 55967744 (53.375MB)
   used     = 48354640 (46.11457824707031MB)
   free     = 7613104 (7.2604217529296875MB)
   86.39733629427693% used
PS Perm Generation
   capacity = 62062592 (59.1875MB)
   used     = 60243112 (57.452308654785156MB)
   free     = 1819480 (1.7351913452148438MB)
   97.06831451706046% used

jstack

jstack(Stack Trace for java) (opens new window)是 Java 堆栈跟踪工具。jstack 用来打印目标 Java 进程中各个线程的栈轨迹,以及这些线程所持有的锁,并可以生成 java 虚拟机当前时刻的线程快照(一般称为 threaddump 或 javacore 文件)。

线程快照是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。

jstack官方文档

命令格式:

jstack [options] pid

常用参数:

  • options: 选项参数
    • F: 当正常输出请求不被响应时,强制输出线程堆栈
    • l: 除堆栈外,显示关于锁的附加信息
    • m: 打印 java 和 jni 框架的所有栈信息
  • pid: 进程ID

jtack示例

$ jstack -F 8321
Attaching to process ID 8321, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 1.6.0-rc-b100
Deadlock Detection:

Found one Java-level deadlock:
=============================

"Thread2":
  waiting to lock Monitor@0x000af398 (Object@0xf819aa10, a java/lang/String),
  which is held by "Thread1"
"Thread1":
  waiting to lock Monitor@0x000af400 (Object@0xf819aa48, a java/lang/String),
  which is held by "Thread2"

Found a total of 1 deadlock.

Thread t@2: (state = BLOCKED)

Thread t@11: (state = BLOCKED)
 - Deadlock$DeadlockMakerThread.run() @bci=108, line=32 (Interpreted frame)

Thread t@10: (state = BLOCKED)
 - Deadlock$DeadlockMakerThread.run() @bci=108, line=32 (Interpreted frame)

Thread t@6: (state = BLOCKED)

Thread t@5: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=-1107318896 (Interpreted frame)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.ref.ReferenceQueue.remove(long) @bci=44, line=116 (Interpreted frame)
 - java.lang.ref.ReferenceQueue.remove() @bci=2, line=132 (Interpreted frame)
 - java.lang.ref.Finalizer$FinalizerThread.run() @bci=3, line=159 (Interpreted frame)

Thread t@4: (state = BLOCKED)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.Object.wait(long) @bci=0 (Interpreted frame)
 - java.lang.Object.wait() @bci=2, line=485 (Interpreted frame)
 - java.lang.ref.Reference$ReferenceHandler.run() @bci=46, line=116 (Interpreted frame)

线程的状态描述:

线程状态 描述
NEW 线程还未启动
RUNNABLE 线程正在JVM中执行
BLOCKED 线程被阻塞,等待监视器锁
WAITING 线程无限期地等待另一个线程执行特定的操作
TIMED_WAITING 线程正在等待另一个线程执行一个动作,等待时间为指定的时间
TERMINATED 线程已经退出

jinfo

jinfo(JVM Configuration info) (opens new window),是 Java 配置信息工具。jinfo 用于实时查看和调整虚拟机运行参数。如传递给 Java 虚拟机的-X(即输出中的 jvm_args)、-XX参数(即输出中的 VM Flags),以及可在 Java 层面通过System.getProperty获取的-D参数(即输出中的 System Properties)。

jinfo官方文档

命令格式:

jinfo [option] pid

常用参数:

  • options: 选项参数
    • flags: 打印传递给JVM的命令行标志。
    • sysprops: 输出系统属性,等同于 System.getProperties()
  • pid: 进程ID

jinfo示例

$ jinfo -sysprops 29527
Attaching to process ID 29527, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.222-b10
...

参考

  1. JVM 命令行工具
  2. Java8官方文档