03-JVM监控及诊断工具-GUI篇

JVM监控及诊断工具-GUI篇

工具概述

使用上一章命令行工具或组合能帮您获取目标Java应用性能相关的基础信息,但它们存在下列局限:

1.无法获取方法级别的分析数据,如方法间的调用关系、各方法的调用次数和调用时间等(这对定位应用性能瓶颈至关重要)。
2.要求用户登录到目标 Java 应用所在的宿主机上,使用起来不是很方便。
3.分析数据通过终端输出,结果展示不够直观。

为此,JDK提供了一些内存泄漏的分析工具,如jconsole,jvisualvm等,用于辅助开发人员定位问题,但是这些工具很多时候并不足以满足快速定位的需求。所以这里我们介绍的工具相对多一些、丰富一些。

图形化综合诊断工具

  • JDK自带的工具
    • jconsole:JDK 自带的可视化监控工具。查看 Java 应用程序的运行概况、监控堆信息、永久区(或元空间)使用情况、类加载情况等
      • 位置:<JAVA_HOME>\bin\jconsole.exe
    • Visual VM:Visual VM 是一个工具,它提供了一个可视界面,用于查看 Java 虚拟机上运行的基于 Java 技术的应用程序的详细信息。
    • JMC:Java Mission Control,内置 Java Flight Recorder。能够以极低的性能开销收集Java虚拟机的性能数据。
  • 第三方工具
    • MAT:MAT(Memory Analyzer Tool)是基于 Eclipse 的内存分析工具,是一个快速、功能丰富的 Java heap 分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
      • Eclipse 的插件形式安装
    • JProfiler:商业软件,需要付费。功能强大。

JConsole

jconsole:从 Java 5 开始,在 JDK 中自带的 Java 监控和管理控制台。用于对 JVM 中内存、线程和类等的监控,是一个基于 JMX(java management extensions) 的 GUI 性能监控工具。

官方文档:https://docs.oracle.com/javase/7/docs/technotes/guides/management/jconsole.html

启动

两种启动方式:

  1. 直接单击 jconsole.exe,仅适用于 Windows
  2. 命令行窗口中输入 jconsole 命令

启动界面如下:

JConsole启动界面JConsole启动界面

需要选择一个要监控的进程。

界面如下:

img

img

img

三种启动方式

  • Local:使用 JConsole 连接一个正在本地系统运行的 JVM,并且执行程序的和运行 JConsole 的需要是同一个用户。JConsole 使用文件系统的授权通过 RMI 连接起链接到平台的 MBean 的服务器上。这种从本地连接的监控能力只有 Sun 的 JDK 具有。
  • Remote:使用下面的 URL 通过 RMI 连接器连接到一个 JMX 代理,service:jmx:rmi:///jndi/rmi://hostName:portNum/jmxrmi。JConsole 为建立连接,需要在环境变量中设置 mx.remote.credentials 来指定用户名和密码,从而进行授权。
  • Advanced:使用一个特殊的 URL 连接 JMX 代理。一般情况使用自己定制的连接器而不是 RMI 提供的连接器来连接 JMX 代理,或者是一个使用 JDK1.4 的实现了 JMX 和 JMX Remote 的应用

Visual VM

  • Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具。
  • 它集成了多个JDK命令行工具,使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat、jstack)等,甚至代替JConsole。
  • 在JDK 6 Update 7以后,Visual VM便作为JDK的一部分发布(VisualVM 在JDK/bin目录下)即:它完全免费。
  • 此外,Visual VM也可以作为独立的软件安装:官方地址:https://visualvm.github.io/index.html

Visual VMVisual VM

插件的安装

IDEA 安装 VisualVM Launcher 插件

“File” →→ “Settings” →→ “Plugins” 打开插件安装界面。

在市场(Marketplace)中搜索 “VisualVM Launcher”

img

插件安装好之后,重启IDEA,然后再打开 “Settings” 配置界面,找到 “VisualVM Launcher”,配置VisualVM的路径和JDK,如下:

img

连接方式

1. 在IDEA中通过插件启动

img

两种启动方式,一种是正常启动,一种是DEBUG启动。

也可启动应用后通过,左侧的 “Start VisualVM” 来启动。

img

启动后的界面如下:

img

img

2. 本地连接

进入到VisualVM安装目录,然后进入到 “bin” 文件夹,单击 “visualvm.exe” 启动 VisualVM。

界面如下

img

在左侧的“Local” 下双击要监控的进程。即可进入监控界面

img

3. 远程连接

img

主要功能

  1. 生成/读取堆内存快照
  2. 查看JVM参数和系统属性
  3. 查看运行中的虚拟机进程
  4. 生成/读取线程快照
  5. 程序资源的实时监控
  6. 其他功能
    • JMX代理连接
    • 远程环境监控
    • CPU分析和内存分析

生成堆内存/线程快照

在左侧的 “Local” 中通过进程生成堆内存/线程快照

在左侧的 “Local” 中右击要生成快照的进程

  • 选择 “Heap Dump” 生成堆内存快照
  • 选择 “Thread Dump” 生成线程快照

img

通过监视(Monitor)生成堆内存快照

img

堆内存快照如下:

img

这些快照存储在内存中,当线程停止的时候快照就会丢失,如果还想利用,可以将快照进行另存为操作,如下图:

img

通过线程(Threads)生成线程快照

img

线程快照如下:

img

这些快照存储在内存中,当线程停止的时候快照就会丢失,如果还想利用,可以将快照进行另存为操作,如下图:

img

载入堆内存/线程快照

img

通过 “File” →→ “Load” 打开选择快照对话框。

img

选择堆内存快照或线程快照即可装入。

JProfiler

基本概述

在运行 Java 的时候有时候想测试运行时占用内存情况,这时候就需要使用测试工具查看了。在 eclipse 里面有 Eclipse Memory Analyzer tool(MAT)插件可以测试,而在 IDEA 中也有这么一个插件,就是 JProfiler。JProfiler 是由 ej-technologies 公司开发的一款 Java 应用性能诊断工具。功能强大,但是收费。

特点:

  • 使用方便、界面操作友好(简单且强大)
  • 对被分析的应用影响小(提供模板)
  • CPU,Thread,Memory 分析功能尤其强大
  • 支持对 jdbc,noSql,jsp,servlet,socket 等进行分析
  • 支持多种模式(离线,在线)的分析
  • 支持监控本地、远程的 JVM
  • 跨平台,拥有多种操作系统的安装版本

img

主要功能:

  1. 方法调用:对方法调用的分析可以帮助您了解应用程序正在做什么,并找到提高其性能的方法
  2. 内存分配:通过分析堆上对象、引用链和垃圾收集能帮您修复内存泄露问题,优化内存使用
  3. 线程和锁:JProfiler 提供多种针对线程和锁的分析视图助您发现多线程问题
  4. 高级子系统:许多性能问题都发生在更高的语义级别上。例如,对于 JDBC 调用,您可能希望找出执行最慢的 SQL 语句。JProfiler 支持对这些子系统进行集成分析

官网地址:https://www.ej-technologies.com/products/jprofiler/overview.html

安装与配置

下载与安装

下载地址:https://www.ej-technologies.com/download/jprofiler/files

img

一个神秘的下载地址:https://downloadlynet.ir/2020/12/1885/03/jprofiler/00/

JProfiler 中配置 IDEA

img

img

img

具体使用

数据采集方式

Profier 数据采集方式分为两种:Sampling(样本采集)和 Instrumentation(重构模式)

  • Instrumentation:这是 JProfiler 全功能模式。在 class 加载之前,JProfier 把相关功能代码写入到需要分析的 class 的 bytecode 中,对正在运行的 jvm 有一定影响。
    • 优点:功能强大。在此设置中,调用堆栈信息是准确的。
    • 缺点:若要分析的 class 较多,则对应用的性能影响较大,CPU 开销可能很高(取决于 Filter 的控制)。因此使用此模式一般配合 Filter 使用,只对特定的类或包进行分析
  • Sampling:类似于样本统计,每隔一定时间(5ms)将每个线程栈中方法栈中的信息统计出来。
    • 优点:对 CPU 的开销非常低,对应用影响小(即使你不配置任何 Filter)
    • 缺点:一些数据/特性不能提供(例如:方法的调用次数、执行时间)

注:JProfiler 本身没有指出数据的采集类型,这里的采集类型是针对方法调用的采集类型。因为 JProfiler 的绝大多数核心功能都依赖方法调用采集的数据,所以可以直接认为是 JProfiler 的数据采集类型。

遥感监测 Telemetries

img

实时内存视图 (Live Memory)

Live memory 内存剖析:class/class instance 的相关信息。例如对象的个数,大小,对象创建的方法执行栈,对象创建的热点。

img

  • 所有对象 All Objects
    显示所有加载的类的列表和在堆上分配的实例数。只有 Java 1.5(JVMTI)才会显示此视图。
  • 记录对象 Record Objects
    查看特定时间段对象的分配,并记录分配的调用堆栈。
  • 分配访问树 Allocation Call Tree
    显示一棵请求树或者方法、类、包或对已选择类有带注释的分配信息的 J2EE 组件。
  • 分配热点 Allocation Hot Spots
    显示一个列表,包括方法、类、包或分配已选类的 J2EE 组件。你可以标注当前值并且显示差异值。对于每个热点都可以显示它的跟踪记录树。
  • 类追踪器 Class Tracker
    类跟踪视图可以包含任意数量的图表,显示选定的类和包的实例与时间。

分析:内存中的对象的情况

  • 频繁创建的 Java 对象:死循环、循环次数过多
  • 存在大的对象:读取文件是,byte[] 应该边读边写。 如果长时间不写出的话,导致 byte[] 过大
  • 存在内存泄漏

[!NOTE]

  1. All Objects 后面的 Size 大小是浅堆大小
  2. Record Objects 在判断内存泄露的时候使用,可以通过观察 Telemetries 中的 Memory,如果里面出现垃圾回收之后的内存占用逐步提高,这就有可能出现内存泄露问题,所以可以使用 Record Objects 查看,但是该分析默认不开启,毕竟占用 CPU 性能太多

堆遍历器 (Heap Walker)

img

img

img

img

img

CPU 视图 (CPU Views)

JProfiler 提供不同的方法来记录访问树以优化性能和细节。线程或者线程组以及线程状况可以被所有的视图选择。所有的视图都可以聚集到方法、类、包或 J2EE 组件等不同层上。

  • 访问树 Call Tree:显示一个积累的自顶向下的树,树中包含所有在 JVM 中已记录的访问队列。JDBC,JMS 和 JNDI 服务请求都被注释在请求树中。请求树可以根据 Servlet 和 JSP 对 URL 的不同需要进行拆分。
  • 热点 Hot Spots:显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,JDBC,JMS 和 JNDI 服务请求以及按照 URL 请求来进行计算。
  • 访问图 Call Graph:显示一个从已选方法、类、包或 J2EE 组件开始的访问队列的图。
  • 方法统计 Method Statistics:显示一段时间内记录的方法的调用时间细节。

img

线程视图 (Threads)

JProfiler通过对线程历史的监控判断其运行状态,并监控是否有线程阻塞产生,还能将一个线程所管理的方法以树状形式呈现。对线程剖析。

  • 线程历史 Thread History:显示一个与线程活动和线程状态在一起的活动时间表。
  • 线程监控 Thread Monitor:显示一个列表,包括所有的活动线程以及它们目前的活动状况。
  • 线程转储 Thread Dumps:显示所有线程的堆栈跟踪。

线程分析主要关心三个方面:

  1. web 容器的线程最大数。比如:Tomcat 的线程容量应该略大于最大并发数。
  2. 线程阻塞
  3. 线程死锁

img

监控和锁 (Monitors &Locks)

所有线程持有锁的情况以及锁的信息。观察 JVM 的内部线程并查看状态:

  • 死锁探测图表 Current Locking Graph:显示 JVM 中的当前死锁图表。
  • 目前使用的监测器 Current Monitors:显示目前使用的监测器并且包括它们的关联线程。
  • 锁定历史图表 Locking History Graph:显示记录在 JVM 中的锁定历史。
  • 历史检测记录 Monitor History:显示重大的等待事件和阻塞事件的历史记录。
  • 监控器使用统计 Monitor Usage Statistics:显示分组监测,线程和监测类的统计监测数据

案例分析

案例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

public class JProfilerTest {
public static void main(String[] args) {
while (true) {
ArrayList list = new ArrayList();
for (int i = 0; i < 500; i++) {
Data data = new Data();
list.add(data);
}
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

class Data {
private int size = 10;
private byte[] buffer = new byte[1024 * 1024];//1mb
private String info = "hello,atguigu";
}

java

案例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

public class MemoryLeak {
public static void main(String[] args) {
while (true) {
ArrayList beanList = new ArrayList();
for (int i = 0; i < 500; i++) {
Bean data = new Bean();
data.list.add(new byte[1024 * 10]);//10kb
beanList.add(data);
}
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}

class Bean {
int size = 10;
String info = "hello,atguigu";
static ArrayList list = new ArrayList();
}

Java Mission Control

历史

在 Oracle 收购 Sun 之前,Oracle 的 JRockit 虚拟机提供了一款叫做 JRockit Mission Control 的虚拟机诊断工具。

在 Oracle 收购 sun 之后,Oracle 公司同时拥有了 Hotspot 和 JRockit 两款虚拟机。根据 Oracle 对于 Java 的战略,在今后的发展中,会将 JRokit 的优秀特性移植到 Hotspot 上。其中一个重要的改进就是在 Sun 的 JDK 中加入了 JRockit 的支持。

在 Oracle JDK 7u40 之后,Mission Control 这款工具己经绑定在 Oracle JDK 中发布。

自 Java11 开始,本节介绍的 JFR 己经开源。但在之前的 Java 版本,JFR 属于 Commercial Feature 通过 Java 虚拟机参数 -XX:+UnlockCommercialFeatures 开启。

概述

Java Mission Control(简称 JMC) , Java 官方提供的性能强劲的工具,是一个用于对 Java 应用程序进行管理、监视、概要分析和故障排除的工具套件。

它包含一个 GUI 客户端以及众多用来收集 Java 虚拟机性能数据的插件如 JMX Console(能够访问用来存放虚拟机齐个于系统运行数据的 MXBeans)以及虚拟机内置的高效 profiling 工具 Java Flight Recorder(JFR)。

JMC 的另一个优点就是:采用取样,而不是传统的代码植入技术,对应用性能的影响非常非常小,完全可以开着 JMC 来做压测(唯一影响可能是 full gc 多了)。

启动

Mission Control 位于 %JAVA_HOME%/bin/jmc.exe

功能:实时监控JVM运行时的状态

如果是远程服务器,使用前要开 JMX。

  • -Dcom.sun.management.jmxremote.port=${YOUR PORT}
  • -Dcom.sun.management.jmxremote
  • -Dcom.sun.management.jmxremote.authenticate=false
  • -Dcom.sun.management.jmxremote.ssl=false
  • -Djava.rmi.server.hostname=${YOUR HOST/IP}

文件 ->连接 ->创建新连接, 填入上面 JMX 参数的 host 和 port

img

Java Flight Recorder

Java Flight Recorder 是 JMC 的其中一个组件。

Java Flight Recorder 能够以极低的性能开销收集 Java 虚拟机的性能数据。

JFR 的性能开销很小,在默认配置下平均低于 1%。与其他工具相比,JFR 能够直接访问虚拟机内的数据,并且不会影响虚拟机的优化。因此,它非常适用于生产环境下满负荷运行的 Java 程序。

时间类型

Java Flight Recorder 和 JDK Mission Control 共同创建了一个完整的工具链。JDKMission Control 可对 Java Flight Recorder 连续收集低水平和详细的运行时信息进行高效详细的分析。

当启用时 JFR将记录运行过程中发生的一系列事件。其中包括Java层面的事件如线程事件、锁事件,以及Java虚拟机内部的事件,如新建对象,垃圾回收和即时编译事件。

按照发生时机以及持续时间来划分,JFR的事件共有四种类型,它们分别为以下四种:

  • 瞬时事件(Instant Event) ,用户关心的是它们发生与否,例如异常、线程启动事件。
  • 持续事件(Duration Event) ,用户关心的是它们的持续时间,例如垃圾回收事件。
  • 计时事件(Timed Event) ,是时长超出指定阈值的持续事件。
  • 取样事件(Sample Event),是周期性取样的事件。

取样事件的其中一个常见例子便是方法抽样(Method Sampling),即每隔一段时问统计各个线程的栈轨迹。如果在这些抽样取得的栈轨迹中存在一个反复出现的方法,那么我们可以推测该方法是热点方法

启动方式

方式1:使用 -XX:StartFlightRecording=参数

第一种是在运行目标 Java 程序时添加 -XX:startFlightRecording=参数

比如: 下面命令中,JFR 将会在 Java 虚拟机启动 5s 后 (对应 delay=5s) 收集数据,持续 20s(对应 duration=28s)。当收集完毕后,JFR 会将收集得到的数据保存至指定的文件中(对应 filename=myrecording.jfr)

1
java -XX:StartFlightRecording=delay=5s,duration=20s,filename=myrecording.jfr,settings=profile MyApp

shell

由于 JFR 将持续收集数据,如果不加以限制,那么 JFR 可能会填满硬盘的所有空间。因此,我们有必要对这种模式下所收集的数据进行限制。

比如:

1
java -XX:StartFlightRecording=maxage=10m,maxsize=100m,name=SomeLabel MyApp

shell

方式2:使用 jcmd 的JFR.*子命令

通过jcmd来让 JFR 开始收集数据、停止收集数据,或者保存所收集的数据,对应的子命令分别为JFR.start, JFR.stop,以及JFR.dump。

1
$ jcmd <PID> JFR.start settings=profile maxage=10m maxsize=150m name=SomeLabel

shell

上述命令运行过后,目标进程中的 JFR 已经开始收集数据。此时,我们可以通过下述命令来导出已经收集到的数据:

1
$ jcmd <PID> JFR.dump name=SomeLabel filename=myrecording.jfr

shell

最后,我们可以通过下述命令关闭目标进程中的 JFR:

1
$ jcmd <PID> JFR.stop name=SomeLabel

shell

img

1、启动飞行记录仪

img

img

2、正式启动

img

img

img

Java Flight Recorder 取样分析

要采用取样,必须先添加参数:

  • -XX:+UnlockCommercialFeatures
  • -XX:+FlightRecorder

JDK9及更高版本

  • -XX:+UnlockCommercialFeatures
  • -XX:+StartFlightRecording

如:-XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:StartFlightRecording=duration=200s

否则:

img

img

提示

第一张图片中的错误是最新版本中的错误,下面的图可能是一个早期版本中的错误

取样时间默认 1 分钟,可自行按需调整,事件设置选为profiling,然后可以设置取样 profile哪些信息,比如:

  • 加上对象数量的统计: Java Virtual Machine →→ Gc →→ Detailed →→ ObjectCount/Object Count after GC
  • 方法调用采样的间隔从 10ms 改为 1ms(但不能低于 1ms,否则会影响性能了): JavaVirtual Machine →→ Profiling →→ Method Profiling Sample/Method SamplingInformation
  • Socket 与 File 采样,10ms 太久,但即使改为 1ms 也未必能抓住什么,可以干脆取消掉:Java Application →→ File Read/FileWrite/Socket Read/Socket Write

img

然后就开始 Profile,到时间后 Profile 结束,会自动把记录下载回来,在 JMC 中展示。

img

从展示信息中,我们大致可以读到内存和 CPU 信息、代码、线程和 IO 等比较重要的信息展示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.ArrayList;
import java.util.Random;

public class OOMTest {
public static void main(String[] args) {
ArrayList<Picture> list = new ArrayList<>();
while(true){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(new Picture(new Random().nextInt(100 * 50)));
}
}
}

class Picture{
private byte[] pixels;

public Picture(int length) {
this.pixels = new byte[length];
}

public byte[] getPixels() {
return pixels;
}

public void setPixels(byte[] pixels) {
this.pixels = pixels;
}
}