jvm之监控工具-命令行工具

435 阅读9分钟

jps

jps(JavaVirtual Machine process Status Toll)查看当前java进程的小工具。

通过 jps -help查看具体有哪些用法。

usage: jps [-help]

       jps [-q] [-mlvV] [<hostid>]


Definitions:

    <hostid>:      <hostname>[:<port>]
  • -q:仅显示进程。
  • -m:输出住函数传入的参数 就是在执行程序时从命令行输入的参数
  • -l:输出应用程序主类完成package名称或jar完整名称。
  • -v:列出jvm参数,启动时手动设置的。

笔者常用的为jps -lv能够输出进程ID、全限定主类名,jar包的完全路径和手动配置的JVM参数,如下: jps -lv

3433 sun.tools.jps.Jps -Dapplication.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home -Xms8m

1262 org.jetbrains.jps.cmdline.Launcher -Xmx700m -Djava.awt.headless=true -Djava.endorsed.dirs="" -Dpreload.project.path=/Users/naigaipaopao/IdeaProjects/study -Dpreload.config.path=/Users/naigaipaopao/Library/Application Support/JetBrains/IntelliJIdea2021.3/options -Dcompile.parallel=false -Drebuild.on.dependency.change=true -Djdt.compiler.useSingleThread=true -Daether.connector.resumeDownloads=false -Dio.netty.initialSeedUniquifier=3850549903300846449 -Dfile.encoding=UTF-8 -Duser.language=zh -Duser.country=CN -Didea.paths.selector=IntelliJIdea2021.3 -Didea.home.path=/Applications/IntelliJ IDEA.app/Contents -Didea.config.path=/Users/naigaipaopao/Library/Application Support/JetBrains/IntelliJIdea2021.3 -Didea.plugins.path=/Users/naigaipaopao/Library/Application Support/JetBrains/IntelliJIdea2021.3/plugins -Djps.log.dir=/Users/naigaipaopao/Library/Logs/JetBrains/IntelliJIdea2021.3/build-log -Djps.fallback.jdk.home=/Applications/IntelliJ IDEA.app/Contents/jbr/Contents/Home -Djps.fallback.jdk.version=11.0.13 -Dio.netty.noUnsafe=true -Djava.io.tmpdir=/

1263 com.study.StudyApplication -XX:PreInflateSpin=10 -Xms100M -XX:TieredStopAtLevel=1 -Xverify:none -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=51029:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8

1183  -Xms128m -Xmx750m -XX:ReservedCodeCacheSize=512m -XX:+IgnoreUnrecognizedVMOptions -XX:+UseG1GC -XX:SoftRefLRUPolicyMSPerMB=50 -XX:CICompilerCount=2 -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -ea -Dsun.io.useCanonCaches=false -Djdk.http.auth.tunneling.disabledSchemes="" -Djdk.attach.allowAttachSelf=true -Djdk.module.illegalAccess.silent=true -Dkotlinx.coroutines.debug=off -XX:ErrorFile=/Users/naigaipaopao/java_error_in_idea_%p.log -XX:HeapDumpPath=/Users/naigaipaopao/java_error_in_idea.hprof -Xmx1024m -javaagent:/Users/naigaipaopao/JetbrainsIdesCrack_5_3_1_KeepMyLic.jar -Djb.vmOptionsFile=/Users/naigaipaopao/Library/Application Support/JetBrains/IntelliJIdea2021.3/idea.vmoptions -Dsplash=true -Didea.home.path=/Applications/IntelliJ IDEA.app/Contents -Didea.jre.check=true -Didea.executable=idea -Djava.system.class.loader=com.intellij.util.lang.PathClassLoader -Didea.paths.selector=IntelliJIdea2021.3 -Didea.vendor.name=JetBrains

jstat

jstat(JVM statistics-Monitoring Tool),用于监视虚拟机各种运行状态信息的命令行工具。

它可以显示本地或者远程虚拟机中的类加载、内存、垃圾收集、JIT编译等运行数据,在没有 GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。

常用参数

  • -class (类加载器)
  • -compiler (JIT)
  • -gc (GC 堆状态)
  • -gccapacity (各区大小)
  • -gccause (最近一次 GC 统计和原因)
  • -gcnew (新区统计)
  • -gcnewcapacity (新区大小)
  • -gcold (老区统计)
  • -gcoldcapacity (老区大小)
  • -gcpermcapacity (永久区大小)
  • -gcutil (GC 统计汇总)
  • -printcompilation (HotSpot 编译统计)

笔者常用的是jstat -gccapacity <pid> 1000 10jstat -gccause <pid> 1000 10

jstat -gccapacity

NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC 

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2

 33792.0 698880.0 223232.0 10240.0 12800.0 195072.0    68608.0  1398272.0    68608.0    68608.0      0.0 1079296.0  35496.0      0.0 1048576.0   4776.0      8     2
  • NGCMN:年轻代最小容量。
  • NGCMX:年轻代最大容量。
  • NGC:当前的年轻代容量。
  • S0C:当前的from区容量。
  • S1C:当前的to区容量。
  • EC:当前的eden区容量。
  • OGCMN:老年代最小容量。
  • OGCMX:老年代最大容量。
  • OGC:当前老年代容量。
  • OC:当前old空间容量。
  • MCMN:最小元空间容量。
  • MCMX:最大元空间容量。
  • MC:当前元空间容量。
  • CCSMN:压缩的类空间最小容量。
  • CCSMX:压缩的类空间最大容量。
  • CCSC:当前压缩的类空间容量。
  • YGC:年轻代GC的次数。
  • FGC:全堆GC的次数。

jstat -gccause

S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC                 

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC               

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC               

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC               

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC               

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC               

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC               

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC               

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC               

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC               

  0.00   0.00  83.70  26.53  95.73  92.72      8    0.111     2    0.072    0.183 Metadata GC Threshold No GC
  • S0:from区利用率占当前容量的百分比。
  • s1:to区利用率占当前容量的百分比。
  • E:eden区利用率占当前容量的百分比。
  • O:老年代区利用率占当前容量的百分比。
  • M:元空间利用率占当前容量的百分比。
  • CCS:压缩后的元空间利用率占当前容量的百分比。
  • YGC:年轻代GC发生的次数。
  • YGCT:年轻代回收的时间。
  • FGC:全堆GC次数。
  • FGCT:全堆GC的时间。
  • GCT:GC花费的总时间。
  • LGCC:上次GC的原因。
  • GCC:当前GC的原因。

jinfo

jinfo(Configuration Info for Java)用于查看和修改虚拟机的参数。

Usage:
    jinfo <option> <pid>
       (to connect to a running process)

where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties System.getProperties()
    <no option>          to print both VM flags and system properties
    -? | -h | --help | -help to print this help message

一般直接jinfo -flags <pid>查看虚拟机参数。能修改的flag可以使用java -XX:+PrintFlagsFianl -version查看manageable的参数表示可以运行时修改。

image.png

jmap

主要用于生产dump文件,还可以查询finalize执行队列、Java堆和永久代的详细信息,如空间使用率、当前使用的是那种收集器等。

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

常用的是jmap -heap <pid>(jdk11需要使用jhsdb jmap --heap --pid <pid>)和 jmap -dump:live,format=b,file=heap.bin <pid>

jstack

jstack(Stack Trace for Java)命令用于生成当前时刻的线程快照。线程快照就是当前虚拟机内每一条线程正在执行的方法的堆栈的集合,生成线程快照的主要目的是定位线程出现时间停顿的原因,入线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。

下面我们直接看上次《Thread源码阅读及相关问题》的死锁例子来看看:

package com.study.thread;

import lombok.SneakyThrows;

public class DeadLock {
    public static void main(String[] args) {

        Object lock1 = new Object();
        Object lock2 = new Object();

        Thread thread1 = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                synchronized (lock1) {
                    System.out.println("thread1成功持有lock1");
                    Thread.sleep(1000);
                    synchronized (lock2) {
                        System.out.println("thread1成功持有lock2");
                    }
                }


            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                synchronized (lock2) {
                    System.out.println("thread2成功持有lock2");
                    Thread.sleep(1000);
                    synchronized (lock1) {
                        System.out.println("thread2成功持有lock1");
                    }
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

像这样的死锁,项目比较大,是很难通过人工排查的,我们下面看看jstack <pid>的输出:

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007f9d8d8142a8 (object 0x00000007957f0f10, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007f9d8d8118b8 (object 0x00000007957f0f20, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at com.study.thread.DeadLock$2.run(DeadLock.java:35)
	- waiting to lock <0x00000007957f0f10> (a java.lang.Object)
	- locked <0x00000007957f0f20> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)
"Thread-0":
	at com.study.thread.DeadLock$1.run(DeadLock.java:19)
	- waiting to lock <0x00000007957f0f20> (a java.lang.Object)
	- locked <0x00000007957f0f10> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

这个告诉了你具体的代码行,等待的锁被谁持有就很容易定位到了。

特别放送-异常分析

如果仔细的读者可以发现一些问题:开始根据jps了解到,笔者自己手动设置了-xms即最小的堆空间。从jstat了解到一共发生了8次YGC,2次FGC。

为啥会发生8次YGC?

通过jstat -gccapactity可以看到,年轻代最小的时候为33M,最大的时候为682M,当前年轻代占用218M。通过jstat -gccause可以看到目前eden的使用率为83.7%。

项目只是启动,还没使用。很明显我们的设置的-xms比较小,我们可以设置为500M试下。

jstat -gccapacity

NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC 

170496.0 698880.0 209920.0 17408.0 20992.0 128512.0   341504.0  1398272.0   341504.0   341504.0      0.0 1087488.0  44072.0      0.0 1048576.0   5928.0      5     2

170496.0 698880.0 209920.0 17408.0 20992.0 128512.0   341504.0  1398272.0   341504.0   341504.0      0.0 1087488.0  44072.0      0.0 1048576.0   5928.0      5     2

170496.0 698880.0 209920.0 17408.0 20992.0 128512.0   341504.0  1398272.0   341504.0   341504.0      0.0 1087488.0  44072.0      0.0 1048576.0   5928.0      5     2

170496.0 698880.0 209920.0 17408.0 20992.0 128512.0   341504.0  1398272.0   341504.0   341504.0      0.0 1087488.0  44072.0      0.0 1048576.0   5928.0      5     2

170496.0 698880.0 209920.0 17408.0 20992.0 128512.0   341504.0  1398272.0   341504.0   341504.0      0.0 1087488.0  44072.0      0.0 1048576.0   5928.0      5     2

170496.0 698880.0 209920.0 17408.0 20992.0 128512.0   341504.0  1398272.0   341504.0   341504.0      0.0 1087488.0  44072.0      0.0 1048576.0   5928.0      5     2

jstat -gccause

S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC                 

  0.00  63.74  19.11   5.79  95.03  92.11      5    0.080     2    0.063    0.142 Allocation Failure   No GC               

  0.00  63.74  19.11   5.79  95.03  92.11      5    0.080     2    0.063    0.142 Allocation Failure   No GC               

  0.00  63.74  19.11   5.79  95.03  92.11      5    0.080     2    0.063    0.142 Allocation Failure   No GC               

  0.00  63.74  19.11   5.79  95.03  92.11      5    0.080     2    0.063    0.142 Allocation Failure   No GC               

  0.00  63.74  19.11   5.79  95.03  92.11      5    0.080     2    0.063    0.142 Allocation Failure   No GC

可以发现,YGC变成了5次,FGC还是2次,从Metadata GC Threshold变成了Allocation Failure

调整后年轻代最小的时候为166.5M,最大的时候为68.25M,当前年轻代占用205M。通过jstat -gccause可以看到目前eden的使用率为19.11%。那为啥还会有5次YGC呢,那是因为默认开启了-XX:+UsePSAdaptiveSurvivorSizePolicy会动态调整,调整是从小开始调整SurvivorSize直到一个合适的值并且因为只设置了-Xms所以整个堆分区都会进行动态调整直到找到一个合适的值,所以调整的过程中发生YGC是正常的,只要-Xms不要给的太小,不然会从一个很小的值开始调整。

为啥会发生2次FGC呢?

FGC发生的原因一般有老年代空间不足和Metaspace区内存达到阈值了,我们先分析下为啥当-Xms100M的时候会出现Metadata GC Threshold

我们先看看有关元空间有关的配置:

  • -XX:MetaspaceSize:元空间初始化值,默认为20M。
  • -XX:MaxMetaspaceSize:默认为最大字节为2^64-1直接是最大容量。
  • -XX:MinMetaspaceFreeRatio:最小的空闲比率,默认为40%,如果比它小就需要进行扩容。
  • -XX:MaxMetaspaceFreeRatio:最大的空间比率,默认为70%,如果大于它就需要进行释放。
  • -XX:MaxMetaspaceExpansion:膨胀最大步长,默认为5.2M。
  • -XX:MinMetaspaceExpansion:膨胀最小步长,0.32M。

我们再看看发生Metadata GC Threshold时的元空间情况:

  • 最小为0M。
  • 最大为1054M。
  • 当前为34.7M。
  • 当前M的占用率为95.73%。

马上又要扩容了,感觉是最小元空间太小了,下面我们试下,使用-Xms100M -XX:MetaspaceSize=100M,下面看看jstat -gccapacity

S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC                 

 99.93   0.00  45.21  30.20  94.85  92.22      8    0.132     0    0.000    0.132 Allocation Failure   No GC               

 99.93   0.00  45.21  30.20  94.85  92.22      8    0.132     0    0.000    0.132 Allocation Failure   No GC               

 99.93   0.00  45.21  30.20  94.85  92.22      8    0.132     0    0.000    0.132 Allocation Failure   No GC               

 99.93   0.00  45.21  30.20  94.85  92.22      8    0.132     0    0.000    0.132 Allocation Failure   No GC               

 99.93   0.00  45.21  30.20  94.85  92.22      8    0.132     0    0.000    0.132 Allocation Failure   No GC

可以发现没有发生FGC了。那么我们继续看看把参数设置成-Xms500M -XX:MetaspaceSize=100M:

S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC                 

 99.99   0.00   9.59   2.84  95.13  93.12      4    0.148     0    0.000    0.148 Allocation Failure   No GC               

 99.99   0.00   9.59   2.84  95.13  93.12      4    0.148     0    0.000    0.148 Allocation Failure   No GC               

 99.99   0.00   9.59   2.84  95.13  93.12      4    0.148     0    0.000    0.148 Allocation Failure   No GC               

 99.99   0.00   9.59   2.84  95.13  93.12      4    0.148     0    0.000    0.148 Allocation Failure   No GC               

 99.99   0.00   9.59   2.84  95.13  93.12      4    0.148     0    0.000    0.148 Allocation Failure   No GC

可以发现,YGC变成了4次,FGC变成了0次,说明修改-Xms和-XX:MetaspaceSize是有效的。

总结

  • 我们可以通过jps了解到有哪些java进程在运行,并且可以看到手动配置的jvm参数
  • 通过jstat我们可以了解到gc相关的信息(各个区的内存分配、使用率、gc原因等)。
  • 我们可以通过jinfo查看和修改(manageable的参数)虚拟参数。
  • 我们可以通过jmp产生dump文件用于分析,还可以了解当前使用的收集器和堆/永久代的详细信息。
  • 我们可以通过jstack查看线程的快照,方便定位停留时间比较长的线程。