jvm · 2022-07-31 0

jvm 相关命令(jinfo、jmap、jstat、jcmd、jstack)

1. jinfo

查看虚拟机配置参数信息

  • jinfo <pid> :打印jvm版本、所有Java System Properties,以及所有VM flags
  • jinfo -flags <pid> :打印所有VM flags
  • jinfo -flag <param-name> <pid> :打印对应的启动参数的值
  • jinfo -flag [+/-]<param-name> <pid> :启用或禁用对应的启动参数
  • jinfo -flag <param-name>=<param-value> <pid> :设置对应的启动参数的值
root@fb1f075c9e90:/opt/app# jinfo -flags 1
VM Flags:
-XX:CICompilerCount=2 -XX:CompressedClassSpaceSize=125829120 -XX:ConcGCThreads=1 -XX:G1ConcRefinementThreads=2 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=2147483648 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=2147483648 -XX:MaxMetaspaceSize=134217728 -XX:MaxNewSize=1073741824 -XX:MetaspaceSize=134217728 -XX:MinHeapDeltaBytes=1048576 -XX:NewSize=1073741824 -XX:NonNMethodCodeHeapSize=5825164 -XX:NonProfiledCodeHeapSize=122916538 -XX:ProfiledCodeHeapSize=122916538 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC

2. jmap

  • jmap -heap <pid>:输出堆内存设置和使用情况(JDK11使用jhsdb jmap --heap --pid <pid>
  • jmap -histo <pid>:输出heap的直方图,包括类名,对象数量,对象占用大小
  • jmap -histo:live <pid>:同上,只输出存活对象信息
root@fb1f075c9e90:/opt/app# jhsdb jmap --heap --pid 1
Attaching to process ID 1, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.13+8

using thread-local object allocation.
Garbage-First (G1) GC with 2 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40    # 最小堆使用比例,堆内存剩余空间低于这个比例时会开始变大
   MaxHeapFreeRatio         = 70    # 最大堆可用比例,当堆内存剩余空间高于这个比例时,会开始变小
   MaxHeapSize              = 2147483648 (2048.0MB)    # 最大堆空间大小
   NewSize                  = 1073741824 (1024.0MB)    # 新生代分配大小
   MaxNewSize               = 1073741824 (1024.0MB)    # 最大可新生代分配大小
   OldSize                  = 5452592 (5.1999969482421875MB)    # 老年代大小
   NewRatio                 = 2    # 新生代(2个Survivor区和Eden区 )与老年代的堆空间比值,表示新生代:老年代=1:2
   SurvivorRatio            = 8    # 两个Survivor区和Eden区的堆空间比值为 8,表示 S0 : S1 :Eden = 1:1:8
   MetaspaceSize            = 134217728 (128.0MB)    # 元空间大小
   CompressedClassSpaceSize = 125829120 (120.0MB)
   MaxMetaspaceSize         = 134217728 (128.0MB)    # 最大元空间大小
   G1HeapRegionSize         = 1048576 (1.0MB)    # 在使用 G1 垃圾回收算法时,JVM 会将 Heap 空间分隔为若干个 Region,该参数用来指定每个 Region 空间的大小

Heap Usage:
G1 Heap:
   regions  = 2048
   capacity = 2147483648 (2048.0MB)    # 容量
   used     = 219873280 (209.6875MB)    # 已使用大小
   free     = 1927610368 (1838.3125MB)    # 剩余大小
   10.2386474609375% used    # 使用比例
G1 Young Generation:
Eden Space:
   regions  = 209
   capacity = 1128267776 (1076.0MB)
   used     = 219152384 (209.0MB)
   free     = 909115392 (867.0MB)
   19.423791821561338% used
Survivor Space:
   regions  = 0
   capacity = 0 (0.0MB)
   used     = 0 (0.0MB)
   free     = 0 (0.0MB)
   0.0% used
G1 Old Generation:
   regions  = 2
   capacity = 1019215872 (972.0MB)
   used     = 0 (0.0MB)
   free     = 1019215872 (972.0MB)
   0.0% used

MetaspaceSize触发FullGC的阈值,默认约为21M,如做了配置,最小阈值为自定义配置大小。空间使用达到阈值,触发FullGC,同时对该值扩大。当然如果元空间实际使用小于阈值,在GC的时候也会对该值缩小。

MaxMetaspaceSize为元空间的最大值,如果设置太小,就会和上面提到的一样,可能会导致频繁FullGC,甚至OOM。

root@fb1f075c9e90:/opt/app# jmap -histo 1
 num     #instances         #bytes  class name (module)
-------------------------------------------------------
   1:       1024126      150754728  [B (java.base@11.0.13)
   2:        471441       11314584  java.lang.String (java.base@11.0.13)
   3:         25482        7009200  [I (java.base@11.0.13)
   4:         80300        5139200  java.net.URL (java.base@11.0.13)
   5:        155803        3739272  java.lang.StringBuilder (java.base@11.0.13)
   6:         44899        2733136  [Ljava.lang.Object; (java.base@11.0.13)
   7:         15053        2610432  [J (java.base@11.0.13)
   8:         81429        2605728  org.springframework.boot.loader.jar.StringSequence
   9:         29343        1643208  jdk.internal.org.objectweb.asm.Item (java.base@11.0.13)
  10:         61768        1482432  org.springframework.boot.loader.jar.JarURLConnection$JarEntryName
  11:         18366        1469280  org.springframework.boot.loader.jar.JarURLConnection
  12:         10302        1315416  [C (java.base@11.0.13)
  13:         48841        1113688  [Ljava.lang.Class; (java.base@11.0.13)
  14:         12527        1102376  java.lang.reflect.Method (java.base@11.0.13)

3. jstat

  • jstat -class <pid>:输出加载类的数量及所占空间信息
  • jstat -gc <pid>:输出gc信息,包括gc次数和时间,内存使用状况(可带时间和显示条目参数)
  • jstat -gcutil <pid> <interval>:垃圾回收统计
root@fb1f075c9e90:/opt/app# jstat -class 1
Loaded  Bytes  Unloaded  Bytes     Time   
  6145 11632.7        0     0.0       3.30
  • Loaded:加载class的数量
  • Bytes:所占用空间大小
  • Unloaded:未加载数量
  • Bytes:未加载占用空间
  • Time:时间
root@fb1f075c9e90:/opt/app# jstat -gc 1
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
 0.0    0.0    0.0    0.0   1101824.0  1024.0   995328.0   10235.5   29568.0 28261.5 3968.0 3448.8      0    0.000   1      0.081   0      0.000    0.081
  • S0C:第一个幸存区的大小
  • S1C:第二个幸存区的大小
  • S0U:第一个幸存区的使用大小
  • S1U:第二个幸存区的使用大小
  • EC:伊甸园区的大小
  • EU:伊甸园区的使用大小
  • OC:老年代大小
  • OU:老年代使用大小
  • MC:方法区大小
  • MU:方法区使用大小
  • CCSC:压缩类空间大小
  • CCSU:压缩类空间使用大小
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间
root@fb1f075c9e90:/opt/app# jstat -gcutil 1
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
  0.00   0.00   0.09   1.03  95.58  86.91      0    0.000     1    0.081     0    0.000    0.081
  • S0:幸存1区当前使用比例
  • S1:幸存2区当前使用比例
  • E:伊甸园区使用比例
  • O:老年代使用比例
  • M:元数据区使用比例
  • CCS:压缩使用比例
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

因为G1的堆布局跟HotSpot VM里其它GC不一样。它只有一组逻辑上的survivor space,而不像其它HotSpot GC一样有两段明确、固定的地址空间用作survivor space——所以用jstat看G1的话肯定是survivor space 0显示0%,survivor space 1显示100%。这个是正常的。
G1在初始化jstat用的计数器的时候就指定了s0永远是0:

4. jcmd

  • jcmd <pid> help 查看进程支持的命令
  • jcmd <pid> VM.version 查看进程的 JVM 版本
  • jcmd <pid> VM.info 查看 JVM 进程信息
  • jcmd <pid> GC.run 建议进程进行垃圾回收
  • jcmd <pid> GC.class_histogram | more 获取类的统计信息,可以看到类名、对象数量、占用空间等
  • jcmd <pid> VM.flags 获取启动参数
  • jcmd <pid> VM.uptime 获取进程到现在运行了多长时间
  • jcmd <pid> Thread.print 查看线程信息
  • jcmd <pid> PerfCounter.print 获取性能相关数据
  • jcmd <pid> GC.heap_dump $PWD/heap.dump 导出堆快照到当前目录,需要生成dump,再使用特定工具去详细进行分析(例如 VisualVm)
  • jcmd <pid> VM.native_memory [summary|detail] [scale=MB] 查看原生内存信息,需要打开nmt,-XX:NativeMemoryTracking=[off | summary | detail] 打开NMT会带来5%-10%的性能损耗
zxm@zxm-pc:~$ jcmd 10170 Thread.print
10170:
2022-08-16 22:39:38
Full thread dump Java HotSpot(TM) 64-Bit Server VM (11.0.9+7-LTS mixed mode):

Threads class SMR info:
_java_thread_list=0x00007f64a8018070, length=27, elements={
0x00007f6534805800, 0x00007f653480a000, 0x00007f653481f000, 0x00007f6534821800,
0x00007f6534823800, 0x00007f65348f1800, 0x00007f6534ad2000, 0x00007f6534ad2800,
0x00007f6534c06000, 0x00007f64e8001000, 0x00007f64c001c000, 0x00007f653547e800,
0x00007f64cc176000, 0x00007f653548d000, 0x00007f6535571800, 0x00007f6535572800,
0x00007f6534029000, 0x00007f64b0004000, 0x00007f64b0002000, 0x00007f64b0084000,
0x00007f64b0035000, 0x00007f64b0065000, 0x00007f64b006b000, 0x00007f64b0073800,
0x00007f64b0069000, 0x00007f64b0018800, 0x00007f64b0056000
}

"Reference Handler" #2 daemon prio=10 os_prio=0 cpu=1.67ms elapsed=802.39s tid=0x00007f6534805800 nid=0x27ce waiting on condition  [0x00007f651030d000]
   java.lang.Thread.State: RUNNABLE
    at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.9/Native Method)
    at java.lang.ref.Reference.processPendingReferences(java.base@11.0.9/Reference.java:241)
    at java.lang.ref.Reference$ReferenceHandler.run(java.base@11.0.9/Reference.java:213)

"Finalizer" #3 daemon prio=8 os_prio=0 cpu=0.60ms elapsed=802.38s tid=0x00007f653480a000 nid=0x27cf in Object.wait()  [0x00007f651020c000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(java.base@11.0.9/Native Method)
    - waiting on <0x0000000080001318> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(java.base@11.0.9/ReferenceQueue.java:155)
    - waiting to re-lock in wait() <0x0000000080001318> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(java.base@11.0.9/ReferenceQueue.java:176)
    at java.lang.ref.Finalizer$FinalizerThread.run(java.base@11.0.9/Finalizer.java:170)
zxm@zxm-pc:~$ jcmd 10170 VM.native_memory summary scale=MB
10170:

Native Memory Tracking:

Total: reserved=2632MB, committed=2247MB
                    # 堆内存,即-Xmx
-                 Java Heap (reserved=2048MB, committed=2048MB)
                            (mmap: reserved=2048MB, committed=2048MB) 
                        # 类加载信息,加载的类与方法信息,其实就是 metaspace,包含两部分:一是 metadata,被-XX:MaxMetaspaceSize    限制最大大小,另外是 class space,被-XX:CompressedClassSpaceSize限制最大大小
-                     Class (reserved=155MB, committed=40MB)
                            (classes #7200)
                            (  instance classes #6656, array classes #544)
                            (malloc=1MB #18744) 
                            (mmap: reserved=154MB, committed=38MB) 
                            (  Metadata:   )
                            (    reserved=34MB, committed=33MB)
                            (    used=32MB)
                            (    free=1MB)
                            (    waste=0MB =0.00%)
                            (  Class space:)
                            (    reserved=120MB, committed=5MB)
                            (    used=4MB)
                            (    free=1MB)
                            (    waste=0MB =0.00%)

                        # 线程栈,线程与线程栈占用内存,每个线程栈占用大小受-Xss,限制,但是总大小没有限制
-                    Thread (reserved=41MB, committed=3MB)
                            (thread #41)
                            (stack: reserved=41MB, committed=3MB)

                        # 代码缓存,JIT 即时编译后(C1 C2 编译器优化)的代码占用内存,受-XX:ReservedCodeCacheSize限制
-                      Code (reserved=243MB, committed=11MB)
                            (malloc=1MB #5369) 
                            (mmap: reserved=242MB, committed=11MB) 

                        # 垃圾回收,垃圾回收占用内存,例如垃圾回收需要的 CardTable,标记数,区域划分记录,还有标记 GC Root 等等,都需要内存。这个不受限制,一般不会很大的。
-                        GC (reserved=132MB, committed=132MB)
                            (malloc=23MB #20018) 
                            (mmap: reserved=109MB, committed=109MB) 

                        # 内部,命令行解析,JVMTI 使用的内存,这个不受限制,一般不会很大的
-                  Internal (reserved=1MB, committed=1MB)
                            (malloc=1MB #5545) 

                        # 符号,常量池占用的大小,字符串常量池受-XX:StringTableSize个数限制,总内存大小不受限制
-                    Symbol (reserved=9MB, committed=9MB)
                            (malloc=7MB #80170) 
                            (arena=2MB #1)

                        # nmt,内存采集本身占用的内存大小,如果没有打开采集,就不会占用,这个不受限制,一般不会很大的
-    Native Memory Tracking (reserved=2MB, committed=2MB)
                            (tracking overhead=2MB)

整个memory主要包含了Java Heap、Class、Thread、Code、GC、Compiler、Internal、Other、Symbol、Native Memory Tracking、Arena Chunk这几部分;其中reserved表示应用可用的内存大小,committed表示应用正在使用的内存大小

Max Memory = eden + survivor + old + String Constant Pool + Code cache + compressed class space + Metaspace + Thread stack(*thread num) + Direct + Mapped + JVM + Native Memory

注意,committed申请的内存并不是说直接占用了物理内存,由于操作系统的内存管理是惰性的,对于已申请的内存虽然会分配地址空间,但并不会直接占用物理内存,真正使用的时候才会映射到实际的物理内存。所以committed > res也是很可能的

5. jstack

jstack是jdk自带的线程堆栈分析工具,使用该命令可以查看或者导出java应用程序中的线程堆栈信息。分析的是当前时刻的线程快照

zxm@zxm-pc:~$ jstack 10170
2022-08-16 22:50:49
Full thread dump Java HotSpot(TM) 64-Bit Server VM (11.0.9+7-LTS mixed mode):

Threads class SMR info:
_java_thread_list=0x00007f64a8018070, length=27, elements={
0x00007f6534805800, 0x00007f653480a000, 0x00007f653481f000, 0x00007f6534821800,
0x00007f6534823800, 0x00007f65348f1800, 0x00007f6534ad2000, 0x00007f6534ad2800,
0x00007f6534c06000, 0x00007f64e8001000, 0x00007f64c001c000, 0x00007f653547e800,
0x00007f64cc176000, 0x00007f653548d000, 0x00007f6535571800, 0x00007f6535572800,
0x00007f6534029000, 0x00007f64b0004000, 0x00007f64b0002000, 0x00007f64b0084000,
0x00007f64b0035000, 0x00007f64b0065000, 0x00007f64b006b000, 0x00007f64b0073800,
0x00007f64b0069000, 0x00007f64b0018800, 0x00007f64b0056000
}

"Reference Handler" #2 daemon prio=10 os_prio=0 cpu=1.67ms elapsed=1473.23s tid=0x00007f6534805800 nid=0x27ce waiting on condition  [0x00007f651030d000]
   java.lang.Thread.State: RUNNABLE
    at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.9/Native Method)
    at java.lang.ref.Reference.processPendingReferences(java.base@11.0.9/Reference.java:241)
    at java.lang.ref.Reference$ReferenceHandler.run(java.base@11.0.9/Reference.java:213)

"Finalizer" #3 daemon prio=8 os_prio=0 cpu=0.60ms elapsed=1473.23s tid=0x00007f653480a000 nid=0x27cf in Object.wait()  [0x00007f651020c000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(java.base@11.0.9/Native Method)
    - waiting on <0x0000000080001318> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(java.base@11.0.9/ReferenceQueue.java:155)
    - waiting to re-lock in wait() <0x0000000080001318> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(java.base@11.0.9/ReferenceQueue.java:176)
    at java.lang.ref.Finalizer$FinalizerThread.run(java.base@11.0.9/Finalizer.java:170)

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 cpu=0.43ms elapsed=1473.22s tid=0x00007f653481f000 nid=0x27d0 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread0" #5 daemon prio=9 os_prio=0 cpu=2497.66ms elapsed=1473.21s tid=0x00007f6534821800 nid=0x27d1 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

"Sweeper thread" #9 daemon prio=9 os_prio=0 cpu=21.56ms elapsed=1473.21s tid=0x00007f6534823800 nid=0x27d2 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE