JVM性能监控

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支持。

相关推荐
行稳方能走远2 小时前
Android java 学习笔记3
android·java
WF_YL2 小时前
IntelliJ IDEA 关闭保存时在文件末尾换行 -(取消保存自动末尾换行)
java·ide·intellij-idea
撩得Android一次心动2 小时前
Android Lifecycle 全面解析:掌握生命周期管理的艺术(1)
android·java·kotlin·lifecycle
lang201509282 小时前
Java高性能缓存库Caffeine全解析
java·缓存·linq
专注于大数据技术栈2 小时前
java学习--什么是线程安全和不安全
java·学习·安全
进击的小菜鸡dd2 小时前
互联网大厂Java面试:从Spring Boot到微服务架构的场景化技术问答
java·spring boot·redis·ci/cd·微服务·消息队列·mybatis
IT19952 小时前
IDEA+JDK11编译SpringCore5源码及替换项目中的Springcore
java·ide·intellij-idea
techzhi2 小时前
IntelliJ IDEA 启动卡顿问题排查与解决
java·ide·intellij-idea