jps:查看虚拟机进程
jps(Java Virtual Machine Process Status Tool)类似 Linux 下的 ps,用于快速查看哪些 Java 应用正在运行,以及它们的进程 ID,这对于进一步使用其他 JVM 工具进行诊断是必要的。
jps 命令格式:
jps [ options ] [ hostid ]
jps 命令示例:

来看一下 jps 的常用选项:
| 选项列表 | 描述 |
|---|---|
| -q | 只输出进程 ID,忽略主类信息 |
| -l | 输出主类全名,或者执行 JAR 包则输出路径 |
| -m | 输出虚拟机进程启动时传递给主类 main() 方法的参数 |
| -v | 输出虚拟机进程启动时的 JVM 参数 |
jstat:查看JVM运行时信息
jstat(Java Virtual Machine Statistics Monitoring Tool)用于监控 JVM 的各种运行时状态信息,提供有关垃圾回收、类加载、JIT 编译等运行数据。
jstat 命令格式为:
jstat [ option vmid [interval[s|ms] [count]] ]
选项 option 主要分为三类:类加载、垃圾收集、运行期编译状况。
①、-class:监视类装载、卸载数量、总空间以及类装载所耗费的时间。
如下示例输出Java进程38604的ClassLoader相关信息。每秒钟统计一次信息,一共输出两次。

-
Loaded:加载的类的数量。
-
Bytes:所有加载类占用的空间大小。
-
Unloaded:卸载的类的数量。
-
Bytes:卸载类的字节数
-
Time:类加载器所花费的时间。
②、-gc:监视 Java堆状况,包括 Eden 区、2 个 Survivor 区、老年代等容量、已用空间、GC 时间合计等信息。
显示gc相关的堆信息,查看gc的次数,及时间。

S0C:年轻代中第一个survivor(幸存区)的容量 (字节)
S1C:年轻代中第二个survivor(幸存区)的容量 (字节)
S0U :年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
S1U :年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
EC :年轻代中Eden(伊甸园)的容量 (字节)
EU :年轻代中Eden(伊甸园)目前已使用空间 (字节)
OC :Old代的容量 (字节)
OU :Old代目前已使用空间 (字节)
MC:metaspace(元空间)的容量 (字节)
MU:metaspace(元空间)目前已使用空间 (字节)
YGC :从应用程序启动到采样时年轻代中gc次数
YGCT :从应用程序启动到采样时年轻代中gc所用时间(s)
FGC :从应用程序启动到采样时old代(全gc)gc次数
FGCT :从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT:从应用程序启动到采样时gc用的总时间(s)
好,我们再来总结一下 jstat 的主要选项,见下表:
| 选项列表 | 描述 |
|---|---|
| -class | 监视类加载、卸载数量、总空间以及类装载所耗费时长 |
| -gc | 监视 Java 堆情况,包括 Eden 区、2 个 Survivor 区、老年代、元空间等,容量、已用空间、垃圾收集时间合计等信息 |
| -gccapacity | 监视内容与-gc 基本一致,但输出主要关注 Java 堆各个区域使用到的最大、最小空间 |
| -gcutil | 监视内容与-gc 基本相同,但输出主要关注已使用空间占总空间的百分比 |
| -gccause | 与 -gcutil 功能一样,但是会额外输出导致上一次垃圾收集产生的原因 |
| -gcnew | 监视新生代垃圾收集情况 |
| -gcnewcapacity | 监视内容与 -gcnew 基本相同,输出主要关注使用到的最大、最小空间 |
| -gcold | 监视老年代垃圾收集情况 |
| -gcoldcapacity | 监视内容与 -gcold 基本相同,输出主要关注使用到的最大、最小空间 |
| -compiler | 输出即时编译器编译过的方法、耗时等信息 |
| -printcompilation | 输出已经被即时编译的方法 |
导出堆到文件jmap
命令jmap是一个多功能的命令。它可以生成Java程序的堆Dump文件,也可以查看堆内对象实例的统计信息、查看ClassIoader的信息以及finalizer队列。
下例使用jmap生成PID为38604的Java程序的对象统计信息,并输出到s.txt文件中。
jmap -histo 38604 >d:\s.txt

可以看到,这个输出显示了内存中的实例数量和合计。
jmap另一个更为重要的功能是得到Java程序的当前堆快照:
jmap -dump:format=b,file=d:\heap.hprof 38604

可以通过多种工具分析该堆文件。比如jhat工具,或者Visual VM、MAT等工具。
JDK自带堆分析工具 jhat
使用jhat工具可以用于分析Java应用程序的堆快照内容。
jhat d:\heap.hprof

jhat在分析完成后,使用HTTP服务器展示其分析结果。在浏览器中访问htp://127.0.0.1:7000

通过这些链接,开发者可以进一步查看所有类信息(包括Java平台的类)、所有类的实例数量以及实例的具体信息。最后,还有一个链接指向OQL查询界面。
通常,导出的堆快照信息非常大,由于信息太多,可能很难通过页面上简单的链接索引找到想要的信息。为此,jhat还支持使用OQL语句对堆快照进行查询。
使用OQL查询出当前Java程序中所有java.io.File 对象的路径。

可视化性能监控工具Visual VM
Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具,它集成了多种性能统计工具的功能,使用Visual VM可以代替jstat、 jmap、 jhat、 jstack, 甚至代替JConsole。
在JDK 6 Update 7以后,Visual VM便作为JDK的一部分发布,即:它完全免费。

打开jdk的安装目录双击

除了本地连接外,Visual VM也支持远程JMX连接。Java 应用程序可以通过以下参数打开
JMX端口:
找到远程的tomcat的bin下的catalina.sh,加入下面的启动参数
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| JAVA_OPTS="-Djava.rmi.server.hostname=192.168.0.108 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" |

第一步添加远程主机

输入对应的ip和端口就可以连接远程的java程序

添加jmx连接



监控概况
通过Visual VM,可以查看应用程序的基本情况,比如进程ID、Main Class、启动参数等,

单击Tab页面上的监视页面,即可监控应用程序CPU、堆、永久区、类加载和线程数的总体情况。通过页面上的"执行垃圾回收"和"堆Dump"按钮还可以手工执行Full GC和生成堆快照,如图所示。

Thread Dump分析
Visual VM的线程页面( 如图所示)可以提供详细的线程信息。单击右上角的" 线程Dump"按钮可以导出当前所有线程的堆栈信息。
如果Visual VM在当前程序中找到死锁,则会以十分显眼的方式在线程页面给予提示,如
死锁程序
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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();// 占用 north try { Thread.sleep (500);// 等待 north 启动 } catch (Exception e) { e.printStackTrace(); } south .lockInterruptibly();// 占用 south System.out .println("car to south has passed" ); } catch (InterruptedException e1) { 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();// 占用 south try { Thread.sleep (500); } catch (Exception e) { e.printStackTrace(); } north .lockInterruptibly();// 占用 north System.out .println("car to north has passed" ); } catch (InterruptedException e1) { System.out .println("car to north is killed" ); } finally { if (north .isHeldByCurrentThread()) north .unlock(); if (south .isHeldByCurrentThread()) south .unlock(); } } } public static void main(String[] args) throws InterruptedException { DeadLock car2south = new DeadLock(south );//2 个线程死锁 DeadLock car2north = new DeadLock(north ); car2south.start(); car2north.start(); Thread.sleep (1000); } } |

性能分析
Visual VM有两个采样器,在"Sampler"页面下,显示了CPU和内存两个性能采样器,用于实时地监控程序信息。CPU采样器可以将CPU占用时间定位到方法,内存采样器可以查看当前程序的堆信息。
CPU

内存

内存快照分析
通过右键菜单中的"堆Dump"选项,可以立即获得当前应用程序的内存快照,如图6.43
所示(相当于jmap命令)。


内存快照分析功能如图所示,在顶部的Tab页中,提供了4个基本功能页:概要、类、
实例和OQL控制台。下面分别介绍它们。
(1)概要页面展示了当前内存的整体信息,包括内存大小、实例总数、类总数等。
(2)在类页面中,以类为索引,显示了每个类的实例数占用空间。
(3)在实例页面中,将显示指定类的所有实例。开发者便可以查看当前内存中,内存数据的实际内容。
(4)OQL控制台提供了更为强大的对象查询功能。有关VisualVM的OQL支持。