性能监控工具-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

查看Java进程——jps命令

jps类似于ps命令,不同的是它只列出系统中所有java应用程序,jps命令格式
jps [-q] [-mlvV] []

性能监控工具-JDK性能监控_第1张图片

查看虚拟机运行时信息——jstat命令

jstat用于观察java应用程序运行时相关信息的工具。它的功能特别,它的基本语法:
jstat -

  • 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: 用于统计输出多少次数据;
    性能监控工具-JDK性能监控_第2张图片

-class

Loaded  Bytes  Unloaded  Bytes     Time
  1546  2835.7        0     0.0       2.06
//loaded 载入类的数量
//Bytes 载入类的合计大小
//Unloaded  卸载类的数量
//第二个bytes 卸载类的合计大小
//time 加载和卸载的时间之和

-compiler

Compiled Failed Invalid   Time   FailedType FailedMethod
     847      0       0     1.28          0
//Compiled 编译任务执行的次数,
//Failed 编译失败的次数,
//Invalid 编译不可用次数
//FailedType 最后一次失败的类型
//FailedMethod 最后一次失败的类名和方法名

-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总耗时

-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表示最大

-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原因

-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区大小

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

jinfo可以用来查看正在运行的java应用程序的扩展参数,甚至支持在运行时,修改部分参数,它的基本语法为:jinfo [option]
它的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
  • 默认为以上所有的操作

- flags : 如-XX:NewSize,-XX:OldSize等虚拟机相关参数.
- sysprops :java.class.path,java.vm.info等…

性能监控工具-JDK性能监控_第3张图片


导出堆到文件——jmap命令

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

jmap [option] 
  (to connect to running process)
jmap [option] 
  (to connect to a core file)
jmap [option] [server_id@]
  (to connect to remote debug server)

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,结果如下图:
性能监控工具-JDK性能监控_第4张图片
,结果中统计了内存中实例对象数和合计。

-dump
-dump功能得到java程序的当前堆快照:jmap -dump:format=b,file=d:/heamp.hprof 8996

-clstats 与 permstat
该命令还可以查看系统的classloader信息,以及classloader的父子关系,以及各个classloader内部加载的类的数量和总大小。$ jmap -clstats 7876
性能监控工具-JDK性能监控_第5张图片

-finalizerinfo
可以观察系统中finalizer队列中的对象,一个不恰当的finalize()方法可能导致对象堆积在finalizer队列里。$ jmap -finalizerinfo 7876
这里写图片描述
,上图的finalizer队列长度为0。


JDK自带的堆分析工具——jhat

使用jhat工具可以分析java应用程序的堆快照内容:$ jhat d:/heap.hprof
性能监控工具-JDK性能监控_第6张图片
,分析的结果通过浏览器 localhost:7000查看。
性能监控工具-JDK性能监控_第7张图片


查看线程堆栈信息——jstack

jstack可以打印出java程序的线程堆栈信息。jstack [option]

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);
	}
}

使用命令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.ReentrantLock$NonfairSync),
  which is held by "south"
"south":
  waiting for ownable synchronizer 0x00000007d6e544d8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "north"

Java stack information for the threads listed above:
===================================================
//省略输出。。。。。
Found 1 deadlock.

远程主机手机——jstatd

一些监控工具支持远程计算机的控制(如jps,jstat),为了开启远程监控,则需要配合使用jstatd工具。
命令jstatd则是一个RMI服务端程序,它的作用相当于对代理服务器,建立本地计算机与远程监控工具的通信。jstatd服务器将本机的java应用程序传递到远程计算机。如图:
性能监控工具-JDK性能监控_第8张图片

它的命令格式: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;
};

使用命令:jstatd -J-Djava.security.policy=all.policy 服务即开启,默认端口1099。jstatd更为详尽的配置
使用jsp.jstat命令即可访问远程服务器的信息。

jps localhost:1099;

jstat -gcutil 460@localhost:1099 //----460为进程号

多功能命令行——jcmd命令

在jdk1.7之后,新增了一个命令行工具jcmd,它是一个多功能工具,用它可以导出堆,查看java进程,导出线程信息,执行GC等。
使用命令:jcmd -l 列出本机所有的虚拟机。
针对每一个虚拟机可以使用:jcmd pid help来列出pid对应虚拟机所支持的命令。
然后挑选出虚拟机支持的任意命令执行:jcmd pid choosed_vm_cmd;
性能监控工具-JDK性能监控_第9张图片

也可是使用:jcmd Mainclass来替代jcmd pid;

虚拟机信息如下:
9196 org.jetbrains.idea.maven.server.RemoteMavenServer
======================================================
$ jcmd 9196 help
等价于
$ jcmd org.jetbrains.idea.maven.server.RemoteMavenServer help

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=            write data to file             java.hprof[{.txt}]
net=:      send data over a socket        off
depth=           stack trace depth              4
interval=          sample interval in ms          10
cutoff=         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          y
verbose=y|n            print messages about dumps     y

  • 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

然后在监控端启动JConsole,选择远程连接:
性能监控工具-JDK性能监控_第10张图片

  • 概览:以折线图的形式展示了堆内存,线程,类,CPU的使用情况。
  • 内存:不仅展示了堆内存的整体信息,更细化到Eden,Survivor,老年代的使用情况。同事也包括非堆区,Perm的使用情况。还提供了一个“执行GC”按钮,可以强制应用程序执行Full GC.
  • 线程:监视应用程序中线程信息,选中线程还可以跟踪线程的堆栈信息,同时还提供了“检测死锁”功能按钮
    性能监控工具-JDK性能监控_第11张图片
  • :显示应用程序已加载和已卸载的类信息。
  • VM概述:展示当前应用程序的运行环境,包括虚拟机类型,版本,虚拟机参数,堆信息等。

可视化性能监控工具Visual VM

visual vm是一个功能强大的多合一的故障诊断和性能监控的可视化工具,使用visual vm可以代替jstat,jmap,jhat,jstack甚至替代jconsole.
使用命令:jvisualvm启动Visual VM.

连接应用程序
Visual VM支持多种方式连接应用程序:

  • 本地连接: 在本地计算机正在运行的java程序。
  • 远程连接: 支持jmx和jstatd方式操作远程连接。具体的配置方式前文都已介绍到。

监控应用程序
选中了应用程序之后,即可看到监控的页面如下图:
性能监控工具-JDK性能监控_第12张图片

  • 概览:主要包含了进程ID,MainClass,启动参数;JVM参数和系统属性等信息。
  • 监视:主要包含了CPU,内存(堆,Perm),类,线程的使用情况折线图。还提供了“执行垃圾回收”和“堆Dump”按钮来强制执行Full GC和生成堆快照文件。
  • 线程:提供线程的详细信息,还可以检测到死锁,提供“线程Dump”按钮,相当于执行jstack,导出当前线程的信息
    性能监控工具-JDK性能监控_第13张图片
  • 抽样器:它有CPU和内存两个性能采样器,用于实时的监控程序信息。根据这个功能可以简单的定位到系统中最消耗资源的函数。
    性能监控工具-JDK性能监控_第14张图片
  • 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服务器
性能监控工具-JDK性能监控_第15张图片

飞行记录器(Flight Recorder)
它通过记录程序在一段时间内的运行情况,将记录结果进行分析和展示,可以进一步对系统的性能进行分析和诊断。要使用飞行记录器,对于要监控的程序必须添加参数:

-XX:+UnlockCommercialFeatures -XX:+FlightRecorder 

性能监控工具-JDK性能监控_第16张图片

你可能感兴趣的:(虚拟机)