讲解JVM的常用命令,为后面讲解JVM日志分析做准备。
前言
JVM比较重要的知识,在前面三篇文章都将完了,GC日志在第一篇文章中也有所提及,这篇文章主要讲解JVM常用的命令,虽然jvm调优成熟的工具已经有很多,但是所有的工具几乎都是依赖于jdk的接口和底层的这些命令,研究这些命令的使用也让我们更能了解jvm构成和特性。
Sun JDK监控和故障处理命令有jps、jstat、jmap、jhat、jstack、jinfo,下面做一一介绍。
jps
JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
命令格式:
jps [options] [hostid]
option参数:
- -l : 输出主类全名或jar路径
- -q : 只输出LVMID
- -m : 输出JVM启动时传递给main()的参数
- -v : 输出JVM启动时显示指定的JVM参数
示例:
82518 sun.tools.jps.Jps -l -m 69981 /Users/mengloulv/Library/Application Support/JetBrains/IntelliJIdea2020.1/plugins/idea-spring-tools/lib/server/language-server.jar 79213 org.apache.catalina.startup.Bootstrap start 79212 org.jetbrains.jps.cmdline.Launcher /Applications/IntelliJ IDEA.app/Contents/lib/asm-all-7.0.1.jar:xxx...
jstat
jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
详见:https://docs.oracle.com/javase/7/docs/technotes/tools/share/jstat.html#gcnew_option
jstat介绍
jstat命令命令格式: jstat [Options] vmid [interval] [count] 命令参数说明: Options,一般使用 -gcutil 或 -gc 查看gc 情况 pid,当前运行的 java进程号 interval,间隔时间,单位为秒或者毫秒 count,打印次数,如果缺省则打印无数次 Options 参数如下: -gc:统计 jdk gc时 heap信息,以使用空间字节数表示 -gcutil:统计 gc时, heap情况,以使用空间的百分比表示 -class:统计 class loader行为信息 -compile:统计编译行为信息 -gccapacity:统计不同 generations(新生代,老年代,持久代)的 heap容量情况 -gccause:统计引起 gc的事件 -gcnew:统计 gc时,新生代的情况 -gcnewcapacity:统计 gc时,新生代 heap容量 -gcold:统计 gc时,老年代的情况 -gcoldcapacity:统计 gc时,老年代 heap容量 -gcpermcapacity:统计 gc时, permanent区 heap容量
jstat使用示例
示例 1:jstat -gc 15 5000 5
每5秒一次显示进程号为15的java进程的GC情况,每5S生成异常,一共生成5次。
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 307200.0 307200.0 24333.5 0.0 2457600.0 945456.5 5120000.0 3462367.2 241360.0 209218.1 26120.0 20538.3 6642 531.164 20 6.874 538.038 307200.0 307200.0 24333.5 0.0 2457600.0 945513.4 5120000.0 3462367.2 241360.0 209218.1 26120.0 20538.3 6642 531.164 20 6.874 538.038 307200.0 307200.0 24333.5 0.0 2457600.0 968130.4 5120000.0 3462367.2 241360.0 209218.1 26120.0 20538.3 6642 531.164 20 6.874 538.038 307200.0 307200.0 24333.5 0.0 2457600.0 1394418.3 5120000.0 3462367.2 241360.0 209218.1 26120.0 20538.3 6642 531.164 20 6.874 538.038 307200.0 307200.0 24333.5 0.0 2457600.0 1867238.5 5120000.0 3462367.2 241360.0 209218.1 26120.0 20538.3 6642 531.164 20 6.874 538.038
S0C:第一个幸存区的大小 S1C:第二个幸存区的大小 S0U:第一个幸存区的使用大小 S1U:第二个幸存区的使用大小 EC:伊甸园区的大小 EU:伊甸园区的使用大小 OC:老年代大小 OU:老年代使用大小 MC:方法区大小 MU:方法区使用大小 CCSC:压缩类空间大小 CCSU:压缩类空间使用大小 YGC:年轻代垃圾回收次数 YGCT:年轻代垃圾回收消耗时间 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间 单位:KB
我可以计算出如下核心数据:
- 第一个幸存区的大小S0C:300M
- 第二个幸存区的大小S1C:300M
- 伊甸园区的大小EC:2400M
- 老年代大小OC:5000M
- 方法区大小MC:236M
- 年轻代垃圾回收消耗时间YGCT:531.164(单位?)
- 老年代垃圾回收消耗时间FGCT:6.874(单位?)
我们再看输出的GC日志:
Heap before GC invocations=6641 (full 10): par new generation total 2764800K, used 2492979K [0x00000005cc000000, 0x0000000687800000, 0x0000000687800000) eden space 2457600K, 100% used [0x00000005cc000000, 0x0000000662000000, 0x0000000662000000) from space 307200K, 11% used [0x0000000674c00000, 0x0000000676e8cc90, 0x0000000687800000) to space 307200K, 0% used [0x0000000662000000, 0x0000000662000000, 0x0000000674c00000) concurrent mark-sweep generation total 5120000K, used 3462278K [0x0000000687800000, 0x00000007c0000000, 0x00000007c0000000) Metaspace used 209218K, capacity 229352K, committed 241360K, reserved 1265664K class space used 20538K, capacity 24038K, committed 26120K, reserved 1048576K 343501.719: [GC (Allocation Failure) 343501.719: [ParNew: 2492979K->24333K(2764800K), 0.0261186 secs] 5955257K->3486700K(7884800K), 0.0262698 secs] [Times: user=0.05 sys=0.01, real=0.03 secs]
可以计算出如下核心数据:
- 第一个幸存区的大小S0C:300M
- 第二个幸存区的大小S1C:300M
- 伊甸园区的大小EC:2400M
- 老年代大小OC:从这里计算不出来
- 方法区大小MC:从这里计算不出来
- GC耗时:30ms
我配置的JAVA_OPTS参数如下:
-Xmx8000M -Xms8000M -Xmn3000M -XX:PermSize=1000M -XX:MaxPermSize=1000M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=69 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:/home/work/logs/applogs/gc.log -javaagent:/home/work/app/prometheus/jmx_prometheus_javaagent-0.12.0.jar=3010:/home/work/app/prometheus/jmx-exporter.yml
这里我有两个疑问:
- 疑问1:其中2764800/1024=2700M,这个2700M是什么呢?是EC+S0C,或者是EC+S1C么?即“新生代当前可以使用的容量”么?(因为每次S0C和S1C,只会使用其中一个)
- 疑问2:其中7884800K/1024=7700M,这个7700M是什么呢?是OC+EC+S0C,或者是EC+S1C么?即老年代 + “新生代当前可以使用的容量”么?
示例2:jstat -gccapacity 15
同-gc,不过还会输出Java堆各区域使用到的最大、最小空间
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC 819200.0 819200.0 819200.0 273024.0 273024.0 273152.0 5324800.0 5324800.0 5324800.0 5324800.0 0.0 1251328.0 223560.0 0.0 1048576.0 22716.0 174 8
NGCMN Minimum new generation capacity (KB). NGCMX Maximum new generation capacity (KB). NGC Current new generation capacity (KB). S0C Current survivor space 0 capacity (KB). S1C Current survivor space 1 capacity (KB). EC Current eden space capacity (KB). OGCMN Minimum old generation capacity (KB). OGCMX Maximum old generation capacity (KB). OGC Current old generation capacity (KB). OC Current old space capacity (KB). PGCMN Minimum permanent generation capacity (KB). PGCMX Maximum Permanent generation capacity (KB). PGC Current Permanent generation capacity (KB). PC Current Permanent space capacity (KB). YGC Number of Young generation GC Events. FGC Number of Full GC Events.
示例3:jstat -gcutil 15
同-gc,不过输出的是已使用空间占总空间的百分比
详见:https://docs.oracle.com/javase/7/docs/technotes/tools/share/jstat.html#gcutil_option
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.32 0.00 27.68 9.86 98.17 96.03 174 24.437 8 0.720 25.157
示例4:jstat -gccause 15
垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因
详见:https://docs.oracle.com/javase/7/docs/technotes/tools/share/jstat.html#gccause_option
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT LGCC GCC 0.32 0.00 43.15 9.86 98.17 96.03 174 24.437 8 0.720 25.157 Allocation Failure No GC
LGCC:最近垃圾回收的原因 GCC:当前垃圾回收的原因
示例5:jstat -gcnew 15
统计新生代的行为
详见:https://docs.oracle.com/javase/7/docs/technotes/tools/share/jstat.html#gcnew_option
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT 273024.0 273024.0 862.5 0.0 15 15 136512.0 273152.0 119572.5 174 24.437
示例6:jstat -gcold 15
统计新生代的行为
详见:https://docs.oracle.com/javase/7/docs/technotes/tools/share/jstat.html#gcold_option
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT 223560.0 219467.9 22716.0 21813.1 5324800.0 524929.7 174 8 0.720 25.157
示例 7:jstat -class 15
监视类装载、卸载数量、总空间以及耗费的时间。
Loaded Bytes Unloaded Bytes Time 39163 74053.8 11505 17286.0 46.52
Loaded : 加载class的数量 Bytes : class字节大小 Unloaded : 未加载class的数量 Bytes : 未加载class的字节大小 Time : 加载时间
示例8:jstat -compiler 15
输出JIT编译过的方法数量耗时等。
Compiled Failed Invalid Time FailedType FailedMethod 53393 4 0 575.86 1 com/mysql/jdbc/AbandonedConnectionCleanupThread run
Compiled : 编译数量 Failed : 编译失败数量 Invalid : 无效数量 Time : 编译耗时 FailedType : 失败类型 FailedMethod : 失败方法的全限定名
jmap
示例1:jmap -dump:live,format=b,file=dump.hprof 15
dump堆到文件,format指定输出格式,live指明是活着的对象,file指定文件名
Dumping heap to /Users/mengloulv/java-workspace/dump.hprof ... Heap dump file created
dump.hprof这个后缀是为了后续可以直接用MAT(Memory Anlysis Tool)打开。
示例2:jmap -finalizerinfo 15
打印等待回收对象的信息
Attaching to process ID 15, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.202-b08 Number of objects pending for finalization: 0
可以看到当前F-QUEUE队列中并没有等待Finalizer线程执行finalizer方法的对象
示例3:jmap -heap 15
打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况,可以用此来判断内存目前的使用情况以及垃圾回收情况,感觉这个非常使用!
Attaching to process ID 15, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.202-b08 using parallel threads in the new generation. using thread-local object allocation. //GC 方式 Concurrent Mark-Sweep GC Heap Configuration: //堆内存初始化配置 MinHeapFreeRatio = 40 /对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40) MaxHeapFreeRatio = 70 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70) MaxHeapSize = 8388608000 (8000.0MB) //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小 NewSize = 3145728000 (3000.0MB) //对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小 MaxNewSize = 3145728000 (3000.0MB) /对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小 OldSize = 5242880000 (5000.0MB) //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的‘老生代’的大小 NewRatio = 2 //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率 SurvivorRatio = 8 //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB //对应jvm启动参数-XX:MaxPermSize=<value>:设置JVM堆的‘永生代’的最大大小 G1HeapRegionSize = 0 (0.0MB) Heap Usage: //堆内存使用情况 New Generation (Eden + 1 Survivor Space): capacity = 2831155200 (2700.0MB) used = 1819140416 (1734.8674926757812MB) free = 1012014784 (965.1325073242188MB) 64.2543515805845% used Eden Space: capacity = 2516582400 (2400.0MB) used = 1795637848 (1712.4536972045898MB) free = 720944552 (687.5463027954102MB) 71.35223738352458% used From Space: capacity = 314572800 (300.0MB) used = 23502568 (22.413795471191406MB) free = 291070232 (277.5862045288086MB) 7.471265157063802% used To Space: capacity = 314572800 (300.0MB) used = 0 (0.0MB) free = 314572800 (300.0MB) 0.0% used concurrent mark-sweep generation: capacity = 5242880000 (5000.0MB) used = 3448095536 (3288.360153198242MB) free = 1794784464 (1711.6398468017578MB) 65.76720306396484% used 101426 interned Strings occupying 10749600 bytes.
示例4:jmap -histo:live 15 | more
打印堆的对象统计,包括对象数、内存大小等等 (因为在dump:live前会进行full gc,如果带上live则只统计活对象,因此不加live的堆大小要大于加live堆的大小 ),仅打印前15行。
num #instances #bytes class name ---------------------------------------------- 1: 938552 113143448 [C 2: 983711 31478752 java.util.HashMap$Node 3: 930339 22328136 java.lang.String 4: 61854 21628224 [B 5: 215981 19006328 java.lang.reflect.Method 6: 200183 18164992 [Ljava.lang.Object; 7: 121341 16297048 [Ljava.util.HashMap$Node; 8: 511306 12919376 [Ljava.lang.String; 9: 169168 9391000 [I 10: 165488 6619520 java.util.LinkedHashMap$Entry 11: 131563 6315024 org.hibernate.hql.internal.ast.tree.Node 12: 122202 5865696 java.util.HashMap 13: 320105 5121680 java.lang.Integer 14: 204087 4898088 java.util.ArrayList 15: 138888 4444416 java.util.concurrent.ConcurrentHashMap$Node ... ...
xml class name是对象类型,说明如下:
B byte C char D double F float I int J long Z boolean [数组,如[I表示int[] [L+类名 其他对象
jhat
jhat(JVM Heap Analysis Tool)命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。
示例:jhat dump.hprof
当执行完毕后:
可以通过Http://localhost:7000访问:
具体排查时需要结合代码,观察是否大量应该被回收的对象在一直被引用或者是否有占用内存特别大的对象无法被回收。一般情况,会down到客户端用工具来分析。
jstack
jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。
如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。
另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。
命令格式:
jstack [option] LVMID
option参数:
-F : 当正常输出请求不被响应时,强制输出线程堆栈 -l : 除堆栈外,显示关于锁的附加信息 -m : 如果调用到本地方法的话,可以显示C/C++的堆栈
示例:jstack -l 5073|more