《实战java虚拟机》06-性能监控工具

第六章 性能监控工具

性能是任何一款软件都需要关注的重要指标。除了软件的基本功能,性能可以说是评价软件优劣的最重要的指标之一。

外科手术刀:JDK性能监控工具

在JDK的开发包中,除了大家熟知的Java.exe和javac.exe,还有一系列辅助工具。

这些工具在JDK安装目录下的bin中。

《实战java虚拟机》06-性能监控工具
JDK内置工具

查看Java进程————jps

jps 命令类似于Linux下的ps命令,但它只用于列出java的进程。

如下例,进程id为75953,类名为Test。

➜  ~ jps   
77649 Jps
77634 Test
75318 
75642 RemoteMavenServer
77626 Launcher

参数 -q可以指定jps只输出进程id,不输出类名。

➜  ~ jps -q
75318
75642
77626
77978

参数 -m 可以用于输出传递给Java进程的参数。

➜  ~ jps -m
75318 
75642 RemoteMavenServer
78286 Jps -m

参数 -v 可以显示传递给Java虚拟机的参数。

➜  ~ jps -v
78742 Jps -Dapplication.home=/Library/Java/JavaVirtualMachines/jdk-11.0.12.jdk/Contents/Home -Xms8m -Djdk.module.main=jdk.jcmd
75318  -Xmx750m -XX:ReservedCodeCacheSize=512m -Xms128m -XX:+UseG1GC -XX:SoftRefLRUPolicyMSPerMB=50 -XX:CICompilerCount=2 -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -ea -Dsun.io.useCanonCaches=false...

参数 -l 可以用于输出主函数的完整路径。

➜  ~ jps -l
79906 jdk.jcmd/sun.tools.jps.Jps
75318 
75642 org.jetbrains.IDEA.maven.server.RemoteMavenServer
77626 org.jetbrains.jps.cmdline.Launcher

注意:jps命令类似于ps命令,但它只列出系统中所有Java应用程序,通过jps命令可以方便地查看Java进程的启动类、传入参数和Java虚拟机参数等信息。

虚拟机运行时信息———— jstat

jstat命令式一个可以用于观察Java应用程序运行时相关信息的工具,可以通过它查看对信息的详细情况。

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
  • option 可以由以下值构成。
    • -class:显示ClassLoader的相关信息
    • -compiler:显示JIT编译的信息
    • -gc:显示与GC相关的信息
    • -gccapacity:显示各个代的容量以及使用情况
    • -gccause:显示垃圾回收相关信息(同-gcutil),同时显示最后一次或正在发生的垃圾回收的诱发原因
    • -gcnew:显示新生代信息
    • -gcnewcapacity:显示新生代大小与使用情况
    • -gcold:显示老年代和永久代的信息
    • -gcoldcapacity:显示老年代大小。
    • -gcpermcapacity:显示永久代大小
    • -gcutil:显示垃圾回收信息
    • -printcompilation:输出JIT编译的方法信息
  • -t参数可以在输出信息前加一个Timestamp列,显示程序运行时间
  • -h参数可以指定在周期性数据输出时,输出多少行数据后输出一个表头信息
  • interval参数用于指定输出统计数据的周期,单位为毫秒
  • count 参数用于指定一共输出多少次数据

下例输出Java进程62797的ClassLoader相关信息。每秒统计一次,一共输出两次。(下面的实例也是同样的用法)

➜  ~ jps
64736 Launcher
64739 Test
57876 
58473 RemoteMavenServer
65356 Jps
➜  ~ jstat -class -t 64739 1000 2
Timestamp       Loaded  Bytes  Unloaded  Bytes     Time   
           85.1    633  1271.8        0     0.0       0.20
           86.1    633  1271.8        0     0.0       0.20

-class的输出中,Loaded表示载入类的数量,Bytes表示载入类的合计大小,Unloaded表示卸载类的数量,第二个Bytes表示卸载类的大小,Time表示加载类和卸载类上所花费的时间。

下例显示了JIT编译信息。

➜  ~ jps                
64736 Launcher
64739 Test
57876 
58473 RemoteMavenServer
65066 Jps
➜  ~ jstat -compiler -t 64739
Timestamp       Compiled Failed Invalid   Time   FailedType FailedMethod
           49.7       67      0       0     0.03          0

Compiled表示编译任务执行的次数,Failed表示编译失败的次数,Invalid便是编译不可用的次数,Time表示编译的总耗时,FailedType表示最后一次编译失败的类型,FailedMethod表示最后一次编译失败的类名和方法名。

下例显示了与GC相关的堆信息的输出。

➜  ~ jps
64736 Launcher
64739 Test
66613 Jps
57876 
58473 RemoteMavenServer
➜  ~ jstat -gc 64739
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
5120.0 5120.0  0.0    0.0   33280.0   5339.1   87552.0      0.0     4480.0 774.9  384.0   75.9       0    0.000   0      0.000   -          -    0.000
  • S0C:s0区的大小(KB)
  • S1C:s1区的大小(KB)
  • S0U:s0区的使用大小(KB)
  • S1U:s1区的使用大小(KB)
  • EC:eden区的大小(KB)
  • EU:eden区的使用大小(KB)
  • OC:老年代大小(KB)
  • OU:老年代使用大小(KB)
  • MC:元数据区(Metaspace)大小(KB)
  • MU:元数据区(Metaspace)使用大小(KB)
  • CCSC:压缩类空间大小(KB)
  • CCSU:压缩类空间使用大小(KB)
  • YGC:年轻代垃圾回收次数
  • YGCT:年轻代垃圾回收消耗时间
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

下例显示了各个代的信息,与-gc相比,它不仅输出各个代的当前大小,也包含了各个代的最大值和最小值。

➜  ~ jps
64736 Launcher
64739 Test
66613 Jps
57876 
58473 RemoteMavenServer
➜  ~ jstat -gccapacity 64739
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC   CGC 
 43520.0 698880.0  43520.0 5120.0 5120.0  33280.0    87552.0  1398272.0    87552.0    87552.0      0.0 1056768.0   4480.0      0.0 1048576.0    384.0      0     0     -
  • NGCMN:新生代最小容量(KB)
  • NGCMX:新生代最大容量(KB)
  • NGC:当前新生代容量(KB)
  • S0C:第一个幸存区大小(KB)
  • S1C:第二个幸存区的大小(KB)
  • EC:伊甸园区的大小(KB)
  • OGCMN:老年代最小容量(KB)
  • OGCMX:老年代最大容量(KB)
  • OGC:当前老年代大小(KB)
  • OC:当前老年代大小(KB)
  • MCMN:最小元数据容量(KB)
  • MCMX:最大元数据容量(KB)
  • MC:当前元数据空间大小(KB)
  • CCSMN:最小压缩类空间大小(KB)
  • CCSMX:最大压缩类空间大小(KB)
  • CCSC:当前压缩类空间大小(KB)
  • YGC:年轻代gc次数
  • FGC:老年代GC次数
  • CGC:总gc次数

下例显示了最近一次GC的原因,以及当前GC的原因。

➜  ~ jps
64736 Launcher
64739 Test
57876 
58473 RemoteMavenServer
70879 Jps
➜  ~ jstat -gccause 64739
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT    LGCC                 GCC                 
  0.00   0.00  16.04   0.00  17.30  19.76      0    0.000     0    0.000     -        -    0.000 No GC                No GC
  • LGCC:上次gc的原因
  • GCC:当前gc的原因

-gcnew可以查看新生代的一些详细信息:

➜  ~ jstat -gcnew 64739
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT  
5120.0 5120.0    0.0    0.0 15  15    0.0  33280.0   5339.1      0    0.000
  • TT:新生代对象晋升到老年代对象的年龄
  • MTT:新生代对象晋升到老年代对象的年龄最大值
  • DSS:所需的survivor区大小

-gcnewcapacity参数可以详细输出新生代各个区的大小信息。

➜  ~ jstat -gcnewcapacity 73171
  NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX        EC      YGC   FGC   CGC 
   43520.0   698880.0    43520.0 232960.0   5120.0 232960.0   5120.0   697856.0    33280.0     0     0     -
  • NGCMN:新生代最小容量
  • NGCMX:新生代最大容量
  • NGC:当前新生代容量
  • S0CMX:s0区的最大值(KB)
  • S0C:当前s0区大小(KB)
  • S1CMX:s1区的最大值(KB)
  • S1C:当前s1区大小(KB)
  • ECMX:eden区最大值(KB)
  • EC:当前eden大小
  • YGC:年轻代垃圾回收次数
  • FGC:老年代回收次数
  • CGC:总gc次数

-gcold用于展现老年代GC的概况。

➜  ~ jstat -gcold 73171        
   MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT    CGC    CGCT     GCT   
  4480.0    774.9    384.0     75.9     87552.0         0.0      0     0    0.000     -        -    0.000

gcoldcapacity用于展示老年代的容量情况。

➜  ~ jstat -gcoldcapacity 73171
OGCMN OGCMX OGC OC YGC FGC FGCT CGC CGCT GCT
87552.0 1398272.0 87552.0 87552.0 0 0 0.000 - - 0.000

-gcutil用于展示GC回收相关信息。

➜  ~ jstat -gcutil 73171         
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT   
  0.00   0.00  16.04   0.00  17.30  19.76      0    0.000     0    0.000     -        -    0.000
  • S0:s0区当前使用比例
  • S1:s1区当前使用比例
  • E:eden区使用比例
  • O:老年代使用比例
  • M:元数据区使用比例
  • CCS:压缩使用比例
  • YGC:年轻代垃圾回收次数
  • FGC:老年代垃圾回收次数
  • FGCT:老年代垃圾回收消耗时间
  • GCT:垃圾回收消耗总时间

查看虚拟机参数————jinfo

jinfo命令可以用来查看正在运行的java应用程序的扩展参数,甚至支持在运行时修改某些参数。

语法:

jinfo <option> <pid>

其中option可以为以下信息。

  • -flag<name>:打印指定Java虚拟机的参数值。
  • -flag[+|-]<name>:设置指定Java虚拟机参数的布尔值。
  • -flag<name>=<value>:设置指定Java虚拟机参数的值。

在很多情况下,Java应用程序不会指定所有的Java虚拟机参数。而此时,开发人员可能不知道某一个具体的java虚拟机参数的默认值。在这种情况下,可以使用 jinfo 命令,开发人员可以很方便地找到Java虚拟机参数的当前值。

下例显示了新生代对象晋升到老年对象的最大年龄。在应用程序启动时,并没有制定这个参数,但通过 jinfo 命令可以查看这个参数的当前值。

➜  ~ jinfo -flag MaxTenuringThreshold 82785
-XX:MaxTenuringThreshold=15

显示是否打印GC详细信息:

➜  ~ jinfo -flag PrintGCDetails 82785      
-XX:-PrintGCDetails

除了查找参数值外,jinfo还可以修改部分参数值。

下例显示了通过jinfo命令对PrintGCdetails参数的修改,它可以在Java程序启动时,动态关闭或者打开这个开关

➜  ~ jinfo -flag PrintGCDetails 82785      
-XX:-PrintGCDetails
➜  ~ jinfo -flag +PrintGCDetails 82785
➜  ~ jinfo -flag PrintGCDetails 82785 
-XX:+PrintGCDetails
➜  ~ jinfo -flag -PrintGCDetails 82785
➜  ~ jinfo -flag PrintGCDetails 82785 
-XX:-PrintGCDetails

注意:使用jinfo命令不仅可以查看运行时某一个Java虚拟机参数的值,甚至可以在运行时修改部分参数,并使之立即生效。

导出堆到文件————jmap

jmap命令是一个多功能的命令,它可以生成java程序的堆dump文件,也可以查看堆内对象实例的统计信息、查看classLoader的信息以及finalizer队列。

下例使用jmap来生成Java程序的对象统计信息,并输出到s.txt中。

➜  ~ jmap -histo 82785 > /Users/jack/Downloads/s.txt

输出内容如下:


 num     #instances         #bytes  class name
----------------------------------------------
   1:           715        2616680  [I
   2:          1873        1463016  [B
   3:          6845         829160  [C
   4:          5364         128736  java.lang.String
   5:           719          81816  java.lang.Class
......
 386:             1             16  sun.util.resources.LocaleData
 387:             1             16  sun.util.resources.LocaleData$LocaleDataResourceBundleControl
Total         24184        5467264

可以看到输出显示了内容的实例数量和统计。

jmap的另一个更重要的功能就是得到Java程序的当前堆快照。

➜  ~ jmap -dump:format=b,file=/Users/jack/Downloads/heap.hprof 82785
Heap dump file created

可以使用jhat或者Visual VM等工具来分析堆文件。

注意:jmap可用于导出Java应用程序的堆快照。

jmap还可以查看系统的ClassLoader的信息。

➜  ~ jmap -clstats 91677
Index Super InstBytes KlassBytes annotations   CpAll MethodCount Bytecodes MethodAll   ROAll   RWAll   Total ClassName
    1    -1    306352        480           0       0           0         0         0      24     584     608 [C
    2    -1    146008        480           0       0           0         0         0      24     584     608 [B
    3    33     96288        624           0    8784          94      4623     25776   12144   24120   36264 java.lang.String
    4    33     81816        648           0   19688         130      4973     25536   16584   31064   47648 java.lang.Class
    5    -1     39792        480           0       0           0         0         0      24     584     608 [Ljava.lang.Object;
    ......
  709   708         0        608           0     760           5        70       792     464    1848    2312 sun.util.resources.TimeZoneNamesBundle
  710    33         0        496           0    1464           6       311      1624    1288    2456    3744 test.Test
               888560     430408        1776 1404464        9083    387217   1821128 1227440 2662416 3889856 Total
                22.8%      11.1%        0.0%   36.1%           -     10.0%     46.8%   31.6%   68.4%  100.0%
Index Super InstBytes KlassBytes annotations   CpAll MethodCount Bytecodes MethodAll   ROAll   RWAll   Total ClassName

jmap查看finalizer列队中的对象,一个不恰当的finalize()函数可能导致对象堆积在finalizer队列中,使用下面命令可以查看堆积在finalizer队列中的对象。

➜  ~ jmap -finalizerinfo 5601
Attaching to process ID 5601, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.221-b11
Number of objects pending for finalization: 0

JDK堆分析工具————jhat

使用jhat命令可以分析Java应用程序的堆快照内容。

说明:jhat命令在JDK9后移除,官网建议使用Visual VM代替。

命令:

jhat /Users/jack/Downloads/heap.hprof

分析完成后,使用浏览器访问https://127.0.0.1:7000查看对应信息。

查看线程堆栈————jstack

jstack命令用于导出Java应用程序的线程堆栈,语法如下:

jstack [-l] <pid>

-l选项用于打印锁的附加信息。

jstack命令会在控制台输出程序中所有锁的信息,可以使用重定向将输出内容保存到文件中。

jstack -l 69478 /Users/jack/Downloads/deadlock.txt

注意:通过jstack命令不仅可以得到线程堆栈,还能自动进行死锁检查,输出找到的死锁信息。

远程主机信息收集————jstatd

之前介绍的都是只涉及监控本机Java应用程序,使用jstatd可以对远程服务进行监控。

jstatd命令是一个RMI服务端程序,作用相当于代理服务器,建立本地计算机与远程监控工具的通信。

《实战java虚拟机》06-性能监控工具
jstatd命令工作示意图

默认情况下jstatd命令会在1099端口开启RMI服务器。

使用jstat显示远程进程460的GC情况:

《实战java虚拟机》06-性能监控工具
jstat

多功能命令行————jcmd

在JDK1.7后,新增了一个命令行工具 jcmd。它是一个多功能的工具,可以用来导出堆、查看java进程、导出线程信息、执行GC等。

下例可以列出所有的Java进程,-l表示列出所有的Java虚拟机。

➜  ~ jcmd -l
8865 test.Test
58473 org.jetbrains.idea.maven.server.RemoteMavenServer
8907 jdk.jcmd/sun.tools.jcmd.JCmd -l

下例查看虚拟机启动时间VM.uptime。

➜  ~ jcmd 8865 VM.uptime
8865:
102.281 s

jcmd命令也可以直接使用MainClass的名字来代替进程号。

下例打印线程栈信息。

➜  ~ jcmd 8865 Thread.print
8865:
2021-09-19 16:29:48
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode):
... 省略所有线程信息

下例查看系统中类的统计信息。

➜  ~ jcmd 8865 GC.class_histogram
8865:

 num     #instances         #bytes  class name
----------------------------------------------
   1:          4016         305776  [C
   2:           474         146008  [B
   3:          4000          96000  java.lang.String
   4:           720          81928  java.lang.Class
   5:           652          39792  [Ljava.lang.Object;
   6:           799          25568  java.util.HashMap$Node
   7:           634          25360  java.util.LinkedHashMap$Entry
   8:           855          20520  java.util.LinkedList$Node
   9:           431          13792  java.util.LinkedList
  10:            30          13280  [Ljava.util.HashMap$Node;
  11:           294          13240  [Ljava.lang.String;
  12:           113           8136  java.lang.reflect.Field
  ......

下例导出堆信息。

➜  ~ jcmd 8865 GC.heap_dump /Users/jack/Downloads/heap.dump
Heap dump file created

下例获取系统的Properties信息。

➜  ~ jcmd 8865 VM.system_properties
8865:
#Sun Sep 19 16:33:42 CST 2021
java.runtime.name=Java(TM) SE Runtime Environment
sun.boot.library.path=/Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home/jre/lib
java.vm.version=25.161-b12
gopherProxySet=false
java.vm.vendor=Oracle Corporation
java.vendor.url=http://java.oracle.com/
......

下例获取启动参数。

➜  ~ jcmd 8865 VM.flags            
8865:
-XX:CICompilerCount=3 -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:MaxNewSize=715653120 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=44564480 -XX:OldSize=89653248 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC 

jcmd拥有jmap的大部分功能,并且在Oracle的官网也推荐使用jcmd代替jmap。


原文始发于微信公众号(Java菜鸟程序员):《实战java虚拟机》06-性能监控工具

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/21070.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!