JDK性能监控工具
作者:互联网
实战java虚拟机
JDK性能监控工具
jdk开发包中,除了比较熟悉的java.exe,javac.exe,还有一系列的辅助工具,它们都存放在jdk安装目录/bin目录下,乍一看这些都是exe,但实际上它们只是将java程序的一层包装,真正的实现是在lib/tools.jar中。以jps命令为例,它实质上是运行:
java -classpath $JAVA_HOME/lib/tools.jar sun.tools.jps.Jps
- 1
查看Java进程——jps命令
jps类似于ps命令,不同的是它只列出系统中所有java应用程序,jps命令格式
jps [-q] [-mlvV] [<hostid>]
查看虚拟机运行时信息——jstat命令
jstat用于观察java应用程序运行时相关信息的工具。它的功能特别,它的基本语法:
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
- option
- -class: 显示classLoader的相关信息
- -compiler: 显示JIT(Just-In-Time Compiler即时编译器)的相关信息
- -gc: 显示与gc相关堆信息
- -gccapacity: 显示各代的容量以及使用情况
- -gccause: 显示垃圾收集相关信息(同 -gcutil),显示最后一次或当前发生的垃圾收集的诱发原因
- -gcnew: 显示新生代信息
- -gcnewcapacity: 显示新生代大小与使用情况
- -gcold :
- -gcoldcapacity
- -gcpermcapacity: 显示永久代的大小 --jdk8之后变更为 -gcmetacapacity
- -gcutil: 显示垃圾收集信息(百分比%)
- -printcompilation: 输出JIT编译的方法信息
- -t: 在输出的信息前加上一个timestamp列,显示程序运行的时间(s)
- -h: 在周期性输出时,输出多少行后,跟着输出一个表头信息
- interver:用于指定输出统计数据的周期,单位是毫秒
- count: 用于统计输出多少次数据;
-class
Loaded Bytes Unloaded Bytes Time
1546 2835.7 0 0.0 2.06
//loaded 载入类的数量
//Bytes 载入类的合计大小
//Unloaded 卸载类的数量
//第二个bytes 卸载类的合计大小
//time 加载和卸载的时间之和
- 1
- 2
- 3
- 4
- 5
- 6
- 7
-compiler
Compiled Failed Invalid Time FailedType FailedMethod
847 0 0 1.28 0
//Compiled 编译任务执行的次数,
//Failed 编译失败的次数,
//Invalid 编译不可用次数
//FailedType 最后一次失败的类型
//FailedMethod 最后一次失败的类名和方法名
- 1
- 2
- 3
- 4
- 5
- 6
- 7
-gc
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
5120.0 5120.0 2208.0 0.0 63488.0 54658.8 84992.0 24.0 8832.0 8388.6 1152.0 984.5 4 0.021 0 0.000 0.021
//S01,S1C,S0U,S1U from,to的大小和已用量大小(KB)
//EC,EU eden的大小和已用量
//OC,OU 老边代的大小和已用量
//PC,PM 永久区大小和已用量
— JDK8之后变为
// MC,MU ,元数据区大小和已用量
//CCSC,CCSU 压缩类空间大小和已用量
//YGC,YGCT: 新生代GC次数和耗时
//FGC,FGCT: Full GC次数和耗时
//GCT GC总耗时
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-gccapacity
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
41984.0 673792.0 73728.0 5120.0 5120.0 63488.0 84992.0 1347584.0 84992.0 84992.0 0.0 1056768.0 8832.0 0.0 1048576.0 1152.0 4 0
//NGCMN ,NGCMX : 新生代最小值和最大值(KB)
//MN:表示最小 ,MX表示最大
- 1
- 2
- 3
- 4
-gccause
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT LGCC GCC
42.81 0.00 86.20 0.05 95.00 85.50 4 0.018 0 0.000 0.018 Allocation Failure No GC
//LGCC 上次GC原因
//GCC 本次GC原因
- 1
- 2
- 3
- 4
- 5
-gcnew
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
5120.0 5120.0 2208.0 0.0 7 15 5120.0 63488.0 54658.8 4 0.021
//TT 新生代晋升到老年代年龄
//MTT 新生代晋升到老年代最大年龄
//DSS 所需的survivor区大小
- 1
- 2
- 3
- 4
- 5
- 6
查看虚拟机参数——jinfo命令
jinfo可以用来查看正在运行的java应用程序的扩展参数,甚至支持在运行时,修改部分参数,它的基本语法为:jinfo [option] <pid>
它的option值可以为:
- -flag 打印指定虚拟机参数[name]的值
- -flag [+|-] to enable or disable the named VM flag
- -flag = to set the named VM flag to the given value
- -flags to print VM flags
- -sysprops to print Java system properties
- <no option> 默认为以上所有的操作
- flags
: 如-XX:NewSize,-XX:OldSize等虚拟机相关参数.
- sysprops
:java.class.path,java.vm.info等…
导出堆到文件——jmap命令
jmap命令是一个多功能命令。它可以生成Java程序的堆Dump文件,也可以查看堆内对象实例的统计信息、查看ClassLoader的信息以及finalizer队列。它的命令格式:
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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
option选项有如下:
- -heap 打印Java堆详细信息
- -histo[:live] 显示堆中对象的统计信息,如果使用了:live子属性,则只统计“存活”对象。
- -clstats 打印类加载器的统计信息(取代了在JDK8之前的 -permstat)
- -permstat Java堆内存的永久保存区域的类加载器的统计信息 ----JDK8之后无此选项
- -finalizerinfo 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象
- -dump: 生成堆转储快照
- -F 当-dump没有响应时,强制生成dump快照
- -J 传递参数给jmap启动的jvm
-histo
统计9336号进程的存活对象统计信息,并保存到文件:$ jmap -histo:live 9336 >d:/dum02.txt
,结果如下图:
,结果中统计了内存中实例对象数和合计。
-dump
-dump功能得到java程序的当前堆快照:jmap -dump:format=b,file=d:/heamp.hprof 8996
-clstats 与 permstat
该命令还可以查看系统的classloader信息,以及classloader的父子关系,以及各个classloader内部加载的类的数量和总大小。$ jmap -clstats 7876
-finalizerinfo
可以观察系统中finalizer队列中的对象,一个不恰当的finalize()方法可能导致对象堆积在finalizer队列里。$ jmap -finalizerinfo 7876
,上图的finalizer队列长度为0。
JDK自带的堆分析工具——jhat
使用jhat工具可以分析java应用程序的堆快照内容:$ jhat d:/heap.hprof
查看线程堆栈信息——jstack
jstack可以打印出java程序的线程堆栈信息。jstack [option] <pid>
option选项信息:
- -F 强制解析线程dump,当程序已经挂起使用
- -m (mixed model)打印java方法和本地方法
- -l 增加打印锁的信息
下例为一个死锁程序,java程序代码如下:
public class DeadLock extends Thread{ protected Object myDirect; static ReentrantLock south = new ReentrantLock(); static ReentrantLock north = new ReentrantLock();
public DeadLock(Object obj) { this.myDirect = obj; if(myDirect == south) { this.setName("south"); } if(myDirect == north) { this.setName("north"); } } @Override public void run() { if(myDirect == south) { try { north.lockInterruptibly(); Thread.sleep(500); south.lockInterruptibly(); System.out.println("car to south has passed"); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("car to south is killed"); } finally { if(north.isHeldByCurrentThread()) { north.unlock(); } if(south.isHeldByCurrentThread()) { south.unlock(); } } } if(myDirect == north) { try { south.lockInterruptibly(); Thread.sleep(500); north.lockInterruptibly(); System.out.println("car to north has passed"); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("car to north is killed"); } finally { if(south.isHeldByCurrentThread()) { south.unlock(); } if(north.isHeldByCurrentThread()) { north.unlock(); } } } } public static void main(String[] args) throws InterruptedException { DeadLock car2south = new DeadLock(south); DeadLock car2north = new DeadLock(north); car2south.start(); car2north.start(); Thread.sleep(1000); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
使用命令jstak -l pid >d:/dead.txt
打印结果:
"north" prio=6 tid=0x00000000069ad000 nid=0x2bcc waiting on condition [0x000000000792f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007d6e54508> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:867)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1201)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:312)
at cn.jhs.chap05.DeadLock.run(DeadLock.java:50)
Locked ownable synchronizers:
- <0x00000007d6e544d8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
“south” prio=6 tid=0x000000000699c800 nid=0x1ed0 waiting on condition [0x000000000782f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007d6e544d8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:867)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1201)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:312)
at cn.jhs.chap05.DeadLock.run(DeadLock.java:31)
Locked ownable synchronizers:
- <0x00000007d6e54508> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
//省略输出。。。。。
Found one Java-level deadlock:
“north”:
waiting for ownable synchronizer 0x00000007d6e54508, (a java.util.concurrent.locks.ReentrantLockNonfairSync),whichisheldby"south""south":waitingforownablesynchronizer0x00000007d6e544d8,(ajava.util.concurrent.locks.ReentrantLockNonfairSync),
which is held by “north”
Java stack information for the threads listed above:
//省略输出。。。。。
Found 1 deadlock.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
远程主机——jstat
一些监控工具支持远程计算机的控制(如jps,jstat),为了开启远程监控,则需要配合使用jstatd工具。
命令jstatd则是一个RMI服务端程序,它的作用相当于对代理服务器,建立本地计算机与远程监控工具的通信。jstatd服务器将本机的java应用程序传递到远程计算机。如图:
它的命令格式:usage: jstatd [-nr] [-p port] [-n rminame]
option选项:
- -nr 当一个存在的RMI Registry没有找到时,不尝试创建一个内部的RMI Registry
- -p port 端口号,默认为1099
- -n rminame 默认为JStatRemoteHost;如果多个jstatd服务开始在同一台主机上,rminame唯一确定一个jstatd服务
- -J 公共jvm参数,可以为jps等命令本身设置其java虚拟机参数。
直接运行jstatd命令会报错,这是因为没有足够的权限所致。可以使用java的安全策略,为其分配相应的权限,并命名为all.policy
grant codebase "file:${JAVA_HOME}\lib\tools.jar" {
permission java.security.AllPermission;
};
- 1
- 2
- 3
使用命令:jstatd -J-Djava.security.policy=all.policy
服务即开启,默认端口1099。jstatd更为详尽的配置
使用jsp.jstat命令即可访问远程服务器的信息。
jps localhost:1099;
jstat -gcutil 460@localhost:1099 //----460为进程号
- 1
- 2
- 3
多功能命令行——jcmd命令
在jdk1.7之后,新增了一个命令行工具jcmd,它是一个多功能工具,用它可以导出堆,查看java进程,导出线程信息,执行GC等。
使用命令:jcmd -l
列出本机所有的虚拟机。
针对每一个虚拟机可以使用:jcmd pid help
来列出pid对应虚拟机所支持的命令。
然后挑选出虚拟机支持的任意命令执行:jcmd pid choosed_vm_cmd
;
也可是使用:jcmd Mainclass
来替代jcmd pid
;
虚拟机信息如下:
9196 org.jetbrains.idea.maven.server.RemoteMavenServer
======================================================
$ jcmd 9196 help
等价于
$ jcmd org.jetbrains.idea.maven.server.RemoteMavenServer help
- 1
- 2
- 3
- 4
- 5
- 6
jcmd拥有jmap大部分功能,并且Oracle官方也推荐使用jcmd替代jmap.
性能统计工具——hprof
HPROF: Heap and CPU Profiling Agent (JVMTI Demonstration Code)
与之前的监控工具不同,hprof不是独立的监控工具,它是一个java agent工具。它可以用于监控java应用程序在运行时的CPU和堆信息。使用java agentlib:hprof=help
命令可以查看hprof的帮助文档。
Option Name and Value Description Default
--------------------- ----------- -------
heap=dump|sites|all heap profiling all
cpu=samples|times|old CPU usage off
monitor=y|n monitor contention n
format=a|b text(txt) or binary output a
file=<file> write data to file java.hprof[{.txt}]
net=<host>:<port> send data over a socket off
depth=<size> stack trace depth 4
interval=<ms> sample interval in ms 10
cutoff=<value> output cutoff point 0.0001
lineno=y|n line number in traces? y
thread=y|n thread in traces? n
doe=y|n dump on exit? y
msa=y|n Solaris micro state accounting n
force=y|n force output to <file> y
verbose=y|n print messages about dumps y
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
java -agentlib:hprof=cpu=times,interval=10 xxx
times选项记录java函数调用前后记录的时间,进而计算函数的执行时间。在当前程序目录下会生成java.hprof.txt文件,记录了性能统计信息。java -agentlib:hprof=heap=dump,format=b,file=d:/x.hprof xx
将应用程序的堆快照保存在指定文件。java -agentlib:hprof=heap=sites xxx
可以输出java应用程序中每个跟踪点上的类所占内存的百分比
图形化虚拟机监控工具JConsole
JConsole是JDK自带的图形化性能监控工具,通过它可以监控堆信息、永久区信息、类加载信息、线程信息、JVM信息等。
连接JAVA程序
JConsole在JAVA_HOME/bin目录下,启动后,会出现新建连接对话框,可以连接本地应用程序,也可以连接远程程序。针对远程程序,需要远程程序在启动时,增加如下参数:
-Djava.rmi.server.hostname=127.0.0.1 //---ipv4地址是:192.1.217.111
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8889
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
- 1
- 2
- 3
- 4
- 5
然后在监控端启动JConsole,选择远程连接:
- 概览:以折线图的形式展示了堆内存,线程,类,CPU的使用情况。
- 内存:不仅展示了堆内存的整体信息,更细化到Eden,Survivor,老年代的使用情况。同事也包括非堆区,Perm的使用情况。还提供了一个“执行GC”按钮,可以强制应用程序执行Full GC.
- 线程:监视应用程序中线程信息,选中线程还可以跟踪线程的堆栈信息,同时还提供了“检测死锁”功能按钮
- 类:显示应用程序已加载和已卸载的类信息。
- VM概述:展示当前应用程序的运行环境,包括虚拟机类型,版本,虚拟机参数,堆信息等。
可视化性能监控工具Visual VM
visual vm是一个功能强大的多合一的故障诊断和性能监控的可视化工具,使用visual vm可以代替jstat,jmap,jhat,jstack甚至替代jconsole.
使用命令:jvisualvm
启动Visual VM.
连接应用程序
Visual VM支持多种方式连接应用程序:
- 本地连接: 在本地计算机正在运行的java程序。
- 远程连接: 支持jmx和jstatd方式操作远程连接。具体的配置方式前文都已介绍到。
监控应用程序
选中了应用程序之后,即可看到监控的页面如下图:
- 概览:主要包含了进程ID,MainClass,启动参数;JVM参数和系统属性等信息。
- 监视:主要包含了CPU,内存(堆,Perm),类,线程的使用情况折线图。还提供了“执行垃圾回收”和“堆Dump”按钮来强制执行Full GC和生成堆快照文件。
- 线程:提供线程的详细信息,还可以检测到死锁,提供“线程Dump”按钮,相当于执行jstack,导出当前线程的信息
- profiler
Visual VM 的 BTrace插件(略)
BTrace是一款非常有意思的工具,它可以在不停机的情况下,通过字节码注入动态的监控系统的运行情况,它可以跟踪方法的调用,构造函数调用和系统内存等信息。
虚拟机诊断工具Mission Control
在Oracle收购sun之前,Oracle的JRockit虚拟机提供了一款JRockit Mission Control的虚拟机诊断工具。在收购sun之后,Oracle拥有了Sun Hotspot 和JRockit两款虚拟机,根据Oracle的战略,在JDK7 Update 40之后,将MissionControl集成到了Hotspot中。它位于$JAVA_HOME/bin/jmc.exe.
MBean服务器
飞行记录器(Flight Recorder)
它通过记录程序在一段时间内的运行情况,将记录结果进行分析和展示,可以进一步对系统的性能进行分析和诊断。要使用飞行记录器,对于要监控的程序必须添加参数:
-XX:+UnlockCommercialFeatures -XX:+FlightRecorder
- 1
标签:java,JDK,虚拟机,信息,util,locks,监控,性能,south 来源: https://blog.csdn.net/weixin_41427294/article/details/123591227