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

相关推荐
MZ_ZXD0016 小时前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php
PP东6 小时前
Flowable学习(二)——Flowable概念学习
java·后端·学习·flowable
ManThink Technology7 小时前
如何使用EBHelper 简化EdgeBus的代码编写?
java·前端·网络
invicinble7 小时前
springboot的核心实现机制原理
java·spring boot·后端
人道领域7 小时前
SSM框架从入门到入土(AOP面向切面编程)
java·开发语言
大模型玩家七七7 小时前
梯度累积真的省显存吗?它换走的是什么成本
java·javascript·数据库·人工智能·深度学习
CodeToGym8 小时前
【Java 办公自动化】Apache POI 入门:手把手教你实现 Excel 导入与导出
java·apache·excel
凡人叶枫8 小时前
C++中智能指针详解(Linux实战版)| 彻底解决内存泄漏,新手也能吃透
java·linux·c语言·开发语言·c++·嵌入式开发
JMchen1238 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
阔皮大师8 小时前
INote轻量文本编辑器
java·javascript·python·c#