JAVA虚拟机之六:虚拟机性能监控和故障处理工具

jopen 8年前

一、JDK命令行工具简介

所有的java开发人员都知道JDK的bin目录下存放着编译java需要的javac,以及运行java程序需要的java这两个命令工具,但是很多人并没有注意到,其实除了这两个工具,该目录下还有很多工具,这些工具其实为我们提供了很多方便且强大的功能,windows7系统64位jdk1.7/bin目录下部分截图:

下面是命令行监控的主要工具和用途:

名称 作用
jps JVM Process Status Tool,现实指定系统内所有的HotSpot虚拟机进程
jstat JVM Statistics Monitoring Tool,用于收集Hotspot虚拟机各个方面的运行参数
jinfo Configuration Info for Java,现实虚拟机配置信息
jmap Memory map for java,生成虚拟机的内存转储快照
jhat JVM heap Dunp Browser,用于分析heapdump文件,他会建立一个HTTP/HTML服务,让用户可通过浏览器查看
jstack Stack Track for java ,显示虚拟机线程快照

1、jps

jps用来查看基于HotSpot的JVM里面中,所有具有访问权限的Java进程的具体状态, 包括进程ID,进程启动的路径及启动参数等等,与unix上的ps类似,只不过jps是用来显示java进程,可以把jps理解为ps的一个子集。 使用jps时,如果没有指定hostid,它只会显示本地环境中所有的Java进程;如果指定了hostid,它就会显示指定hostid上面的java进程,不过这需要远程服务上开启了jstatd服务,可以参看前面的jstatd章节来启动jstad服务。

命令格式 :jps [ options ] [ hostid ] 

参数说明 :

-q 忽略输出的类名、Jar名以及传递给main方法的参数,只输出pid。

-m 输出传递给main方法的参数,如果是内嵌的JVM则输出为null。

-l 输出应用程序主类的完整包名,或者是应用程序JAR文件的完整路径。

-v 输出传给JVM的参数。

-V 输出通过标记的文件传递给JVM的参数(.hotspotrc文件,或者是通过参数-XX:Flags=<filename>指定的文件)。

-J 用于传递jvm选项到由javac调用的java加载器中,例如,“-J-Xms48m”将把启动内存设置为48M,使用-J选项可以非常方便的向基于Java的开发的底层虚拟机应用程序传递参数。下面样例均在linux的jdk1.7下测试。

使用样例:

[root@tools138 ~]# jps

2897 Bootstrap

22558 Jps

[root@tools138 ~]# jps -l

2897 org.apache.catalina.startup.Bootstrap

22568 sun.tools.jps.Jps

[root@tools138 ~]# jps -v

2897 Bootstrap -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/tomcat/endorsed -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp

22578 Jps -Denv.class.path=/usr/java/jdk1.7.0/lib -Dapplication.home=/usr/java/jdk1.7.0 -Xms8m

2、jstat

Jstat用于监控基于HotSpot的JVM,对其堆的使用情况进行实时的命令行的统计,使用jstat我们可以对指定的JVM做如下监控:

- 类的加载及卸载情况

- 查看新生代、老生代及持久代的容量及使用情况

- 查看新生代、老生代及持久代的垃圾收集情况,包括垃圾回收的次数及垃圾回收所占用的时间

- 查看新生代中Eden区及Survior区中容量及分配情况等

jstat工具特别强大,它有众多的可选项,通过提供多种不同的监控维度,使我们可以从不同的维度来了解到当前JVM堆的使用情况。详细查看堆内各个部分的使用量,使用的时候必须加上待统计的Java进程号,可选的不同维度参数以及可选的统计频率参数。

命令格式:

jstat [ option vmid [interval][s|ms][count]]

option 参数如下面表格

Option Displays...
class 用于查看类加载情况的统计
compiler 用于查看HotSpot中即时编译器编译情况的统计
gc 用于查看JVM中堆的垃圾收集情况的统计
gccapacity 用于查看新生代、老生代及持久代的存储容量情况
gccause 用于查看垃圾收集的统计情况(这个和-gcutil选项一样),如果有发生垃圾收集,它还会显示最后一次及当前正在发生垃圾收集的原因。
gcnew 用于查看新生代垃圾收集的情况
gcnewcapacity 用于查看新生代的存储容量情况
gcold 用于查看老生代及持久代发生GC的情况
gcoldcapacity 用于查看老生代的容量
gcpermcapacity 用于查看持久代的容量
gcutil 用于查看新生代、老生代及持代垃圾收集的情况
printcompilation HotSpot编译方法的统计

interval 和count 代表查询次数和间隔。

使用样例:

[root@tools138 ~]# jstat -class 2897

Loaded  Bytes  Unloaded  Bytes     Time  

67431 113866.2    59850 98607.5    1884.07

[root@tools138 ~]# jstat -compiler  2897

Compiled Failed Invalid   Time   FailedType FailedMethod

3782      1       0   507.88          1 org/apache/tomcat/util/IntrospectionUtils setProperty

表示查询系统进程为2897的java程序gc,每100毫秒查询一次,一共查询十次,显示结果每列的含义如下: 

列名 说明
S0C 新生代中Survivor space中S0当前容量的大小(KB)
S1C 新生代中Survivor space中S1当前容量的大小(KB)
S0U 新生代中Survivor space中S0容量使用的大小(KB)
S1U 新生代中Survivor space中S1容量使用的大小(KB)
EC Eden space当前容量的大小(KB)
EU Eden space容量使用的大小(KB)
OC Old space当前容量的大小(KB)
OU Old space使用容量的大小(KB)
PC Permanent space当前容量的大小(KB)
PU Permanent space使用容量的大小(KB)
YGC 从应用程序启动到采样时发生 Young GC 的次数
YGCT 从应用程序启动到采样时 Young GC 所用的时间(秒)
FGC 从应用程序启动到采样时发生 Full GC 的次数
FGCT 从应用程序启动到采样时 Full GC 所用的时间(秒)
GCT T从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC
其他以gc开始的结果列跟gc选项结果列基本一样,这里不一一列举了。

3、jinfo

jinfo可以输出并修改运行时的java 进程的opts。用处比较简单,用于输出JAVA系统参数及命令行参数。

命令格式:

jinfo [option] pid

使用样例:

[root@tools138 ~]# jinfo  -flag  MaxNewSize  2897

-XX:MaxNewSize=18446744073709486080

4、jmap

jmap用于生成堆转储快照(一般称为heapdump或者dump文件)。当然也可其他方法比如加参数-XX:+HeapDumpOnOutOfMemoryError参数,在虚拟机OOM异常的之后自动生成dump文件,也可以通过-XX:+HeapDumpOnCtrlBreak参数则可以使用Ctrl+Break键让虚拟机生成dump文件。在前文《 JAVA虚拟机之3:CMS垃圾收集器》测试中就有生成。dump文件生成后可借助jha、MAT( Eclipse Memory Analyzer tool)、IBM HeapAnalyzer来对dump分析。jmap不仅能获取dump还可以查询finalize执行队列,java堆和永久代详细信息,空间使用率,当前用的是什么收集器等。

jmap -J-d64 -heap pid

命令格式:

jmap [ option ] pid

参数说明:

-dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件.

-finalizerinfo 打印正等候回收的对象的信息.

-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况.

-histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.

-permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来.

-F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效.

使用样例:

jmap -dump:format=b,file=eclipse.bin

[root@tools138 ~]# jmap -dump:format=b,file=eclipse.bin  2897

Dumping heap to /root/eclipse.bin ...

Heap dump file created

5、jhat

jhat是sun提供的dump分析工具,上面讲过分析dump的工具还有MAT( Eclipse Memory Analyzer tool)、IBM HeapAnalyzer等,一般这个命令不太用到,是因为分析dump是个既耗时又耗机器资源的过程,第二个原因是这个工具比较简陋,没有MAT( Eclipse Memory Analyzer tool)、IBM HeapAnalyzer这些专业和强大。

命令格式:

jhat file

使用样例:

防止影响服务,所以在本地windows下使用,先导出dump文件

C:\Users\hz>jmap -dump:format=b,file=test.bin 5152

Dumping heap to C:\Users\hz\test.bin ...

Heap dump file created

然后分析:

C:\Users\hz>jhat test.bin

Reading from test.bin...

Dump file created Wed Dec 30 13:29:19 CST 2015

Snapshot read, resolving...

Resolving 9692 objects...

Chasing references, expect 1 dots.

Eliminating duplicate references.

Snapshot resolved.

Started HTTP server on port 7000

Server is ready.

载在浏览器中输入localhost:7000查看结果,如下图。

6、jstack

jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使用方式只支持以下的这种方式:

如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。 

命令格式 :

jstack [ option ] pid

参数说明:

-F当’jstack [-l] pid’没有相应的时候强制打印栈信息

-l长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表.

-m打印java和native c/c++框架的所有栈信息.

使用样例:

[root@tts217 ~]# jstack -l 29984

.....

</div>

"main" prio=10 tid=0x00007fe648009000 nid=0x7521 runnable [0x00007fe64e753000]

java.lang.Thread.State: RUNNABLE

at java.net.PlainSocketImpl.socketAccept(Native Method)

at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)

at java.net.ServerSocket.implAccept(ServerSocket.java:530)

at java.net.ServerSocket.accept(ServerSocket.java:498)

at org.apache.catalina.core.StandardServer.await(StandardServer.java:453)

at org.apache.catalina.startup.Catalina.await(Catalina.java:777)

at org.apache.catalina.startup.Catalina.start(Catalina.java:723)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:606)

at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:321)

at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:455)

Locked ownable synchronizers:

- None

"VM Thread" prio=10 tid=0x00007fe64806a800 nid=0x7522 runnable

"VM Periodic Task Thread" prio=10 tid=0x00007fe6480a5800 nid=0x7529 waiting on condition

JNI global references: 238

二、JDK可视化工具

1、jconsole

JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,不过此JVM需要使用可管理的模式启动。如果要把一个应用以可管理的形式启动,可以在启动是设置com.sun.management.jmxremote。

jconsole可以选择本地连接,来查看本地java程序参数,也可以连接远程机器来使用,下面连接本地看:

a、概述:有关堆内存使用情况、线程、类加载和CPU使用情况的综述。如下图所示:

b、内存:堆内存和其他内存。如下图所示:

c、线程:峰值/活动线程,在此页面可以查看到各个线程的明细,也可以进行死锁检测。如下图所示:

d、类:监控加载和卸载的类,这个需要综合其他工具进行具体的分析。

e、VM摘要:有关JVM的明细信息。

f、Mbean:当前Java程序的Mbean的操作。

在jconsole里主要可以用来监控内存和线程监控,内存监控相当于jstat,曲线可以选择

可以手动执行gc。线程监控可用来检测死锁。

死锁代码如下:

package jvm;  /**   * 描述:   *   * @author alaric 2016年1月1日 上午12:43:23   */  public class A implements Runnable {   int a, b;   public A(int a, int b) {    this.a = a;    this.b = b;   }   /*    * (non-Javadoc)    *    * @see java.lang.Runnable#run()    */   @Override   public void run() {    // 描述    synchronized (Integer.valueOf(a)) {     synchronized (Integer.valueOf(b)) {      System.out.println(" a + b = " + (a + b));     }    }   }   public static void main(String[] args) {    for (int i = 0; i < 100; i++) {     new Thread(new A(1, 2)).start();     new Thread(new A(2, 1)).start();    }   }  }

多运行几次会出现死锁,程序会阻塞。然后用线程监控,点击检测死锁,发现Thread-199和Thread-198相互等待释放Integer.value(1)或者Integer.value(2)。因为在200次的代码执行中Integer.value()就返回了两个对象。

2、jvisualVM

jvisualVM所谓多合一虚拟机故障处理工具,有强大的插件扩展功能,通过安装插件扩展支持,jvisualVM可以做到:

a、显示虚拟机进程及进程的配置和环境信息(jps,jinfo);

b、监视应用程序CPU、GC、堆、方法区及线程的信息(jstat、jstack);

c、dump及分析堆转储快照(jmap、jhat);

d、方法级的程序性能分析,找出调用最多,运行时间最长的方法;

.....其它通过插件可以做到的;

概述里面可以看到虚拟机版本及配置的参数等。

在监视里可以看cpu、堆、线程类的相关数据。可以执行垃圾回收和dump堆。

在线程里可以看到所有线程,包括运行的、休眠的、等待的、驻留的等,包括运行时间。同时可以dump线程。

在抽样器中可以监控到每个方法执行的时间,还可以对方法过滤。点击线程cpu时间,可以看到每个线程cpu时间。

VisualGC这里可以看到虚拟机运行时每个区域的容量大小,已占用的大小,youngGC和fullGC的次数,以及编译时间、类加载数量和加载时间等。

三、总结:

jdk提供的vm故障处理工具都比较实用,常用的jps,jstat,jmap,jstack以及可视化工具visualvm,当然根据个人实际实用情况,可能还选用第三方的工具进行dump分析,如eclipse的MAT(Memory Analyzer Tool)等。灵活实用这些工具,可以给处理问题带来很大的便利。

</div>

来自: http://alaric.iteye.com/blog/2268015