第六章 性能监控工具
性能是任何一款软件都需要关注的重要指标。除了软件的基本功能,性能可以说是评价软件优劣的最重要的指标之一。
外科手术刀:JDK性能监控工具
在JDK的开发包中,除了大家熟知的java.exe和javac.exe,还有一系列辅助工具。
这些工具在JDK安装目录下的bin中。

查看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服务端程序,作用相当于代理服务器,建立本地计算机与远程监控工具的通信。

默认情况下jstatd
命令会在1099端口开启RMI服务器。
使用jstat
显示远程进程460的GC情况:

多功能命令行————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