type
status
date
slug
summary
tags
category
icon
password
1、JFR和JMC是什么
1.1 JFR是什么
JFR(Java Flight Recorder,下文简称JFR)数据是JVM的历史事件,用来诊断JVM的历史性能和操作。是一种监控工具,可以在Java应用程序执行期间收集有关JVM事件的信息。JFR会开启一组事件,当有对应的事件发生时,就会保留相应的数据到文件中或者内存中(假如有开启缓存池),JMC可以显示这些事件——实时从JVM获取或者从文件中获取,JMC可以展示详细的JFR记录的数据。JFR对于被监控的应用程序来说,默认设置的性能开销很低:程序性能的1%以下,但是随着开始的事件或者记录线程增多,性能开销也会随之增多。
JFR有两种主要的概念:事件和数据流
1.1.1 事件
JFR在Java应用运行时收集对应发生的事件,主要有三种类型的事件提供给JFR收集:
- 即时事件:一旦事件发生会立即进行数据记录
- 持续事件:如果持续时间超过指定阈值则进行数据记录
- 简单事件:用于记录应用所在系统的活跃指标(例如CPU,内存等)
1.1.2 数据流
JFR 收集的事件包含大量数据,将这些数据保存在filename.jfr中,众所周知,磁盘I/O操作非常昂贵。因此,在将数据块刷新到磁盘之前,JFR使用各种缓存来存储收集的数据。因为加入缓存的原因,在某些情况下,JFR的数据存在丢失的可能性。如果发生丢失数据的情况,JFR会尝试通知输出文件,丢失了一部分的信息。
1.2 JMC是什么
Java Mission Control(JMC)是一个强大的 Java 应用程序分析工具,从 2018 年开始,JMC 被 Oracle 开源作为独立工具发布。JMC 是一款可以用于管理、监听、分析以及排除 Java 故障的高级工具,使用 JMC 可以对代码性能、内存分配、GC 延迟、CPU 使用等情况进行低开销的、高效且详细的分析。
JMC 一般搭配 JFR 使用,对 JFR 文件进行可视化展示,适用于以下场景:
- 性能调优:定位CPU、内存或I/O瓶颈。
- 故障诊断:分析崩溃、死锁或延迟问题。
- 生产监控:通过JFR持续记录关键指标。
2、JFR和JMC使用步骤
2.1 下载安装 JMC
在使用 JMC 和 JFR 之前的时候要注意 Java 的版本。
- Java 7 的 7u271 版本和 Java 8 的 8u261 版本之前默认自带了 Java Mission Control (JMC),默认是 JMC 5.5 版本。后续其他版本不再捆绑安装 Java Mission Control,需要另外单独下载安装。
- JMC 8.10 开始,所依赖的最低 JDK 版本为 JDK11,如果是 JDK 8 环境,可以下载 JMC 8.0 版本。
- Java 8 的 8u262 版本之后才支持 JFR。
如果要使用 Java 8 自带的 JMC,默认情况下 JMC 位于:
- macOS
- windows
如果要单独安装JMC,参考以下地址:
- Oracle 官方地址:https://www.oracle.com/java/technologies/jdk-mission-control.htmld
- Oracle JMC 下载:https://www.oracle.com/java/technologies/javase/products-jmc8-downloads.html
在 macOS 系统下单独下载的 JMC 打开如果报错 Failed to create the Java Virtual Machine,右键显示包内容
,进入以下目录JDK Mission Control.app/Contents/MacOS
,运行jmc
文件。
2.2 应用开启JFR功能
第一种方式,使用 JVM 参数开启:
在Tomcat中配置对应的参数不生效,也不知道是什么原因,如果有解决的小伙伴可以在评论区
第二种方式,使用命令行开启:
2.3 应用开始记录JFR文件
延迟 10s 开始记录,记录 10 分钟,保存到
recording.jfr
文件。2.4 使用JMC分析JFR文件
使用 JMC 打开
recording.jfr
文件。
3、JFR 命令行详解
jcmd命令中包含了操作JFR的所有操作,现在对操作以及参数进行详细解释。
首先,假如你不了解一些命令行的使用规则,你可以使用jcmd help命令去了解对应命令行的使用解释。例如,查看一个JFR.check的命令行使用方式,指定对应的进程ID(本文中使用5361),使用如下命令行:
通过英文的意思,我们可以了解到各个参数的使用方式,以及对应含义和注意事项。
下面我们进入正题,与JFR关联的jcmd命令有以下四种:
- JFR.start——启动一个新的JFR记录线程
- JFR.check——检查正在运行的JFR记录线程
- JFR.stop——停止一个指定的JFR记录线程
- JFR.dump——拷贝一个指定的JFR记录线程的内容进入文件中
每个命令行都有对应的参数,现在一一介绍对应的参数详解
3.1 启动JFR
使用
JFR.start
诊断命令启动 flight 记录。例如:参数 | 描述 | 数据类型 | 默认值 |
name | 记录的名称 | 字符串 | - |
设置 | 服务器端模板 | 字符串 | - |
duration | 记录持续时间 | Time | 0s |
filename | 生成记录文件名 | 字符串 | - |
maxAge | 缓冲数据的最长期限 | Time | 0s |
maxsize | 缓冲区的最大大小(以字节为单位) | Long | 0 |
dumponexit | JVM 关闭时运行的转储记录 | 布尔值 | - |
path-to-gc-roots | 收集垃圾收集器 root 的路径 | 布尔值 | False |
3.2 停止JFR
使用
JFR.stop
诊断命令停止运行 flight 记录。例如:参数 | 描述 | 数据类型 | 默认值 |
name | 记录的名称 | 字符串 | - |
filename | 将记录数据复制到文件中 | 字符串 | - |
3.3 检查JFR
使用
JFR.check
命令显示正在进行中的记录的信息。例如:参数 | 描述 | 数据类型 | 默认值 |
name | 记录的名称 | 字符串 | - |
filename | 将记录数据复制到文件中 | 字符串 | - |
maxAge | 转储文件的最大持续时间 | Time | 0s |
maxsize | 转储的最大字节数 | Long | 0 |
begin | 开始转储数据的时间 | 字符串 | - |
end | 转储数据的结束时间 | 字符串 | - |
path-to-gc-roots | 收集垃圾收集器 root 的路径 | 布尔值 | false |
3.4 dump JFR
使用
JFR.dump
诊断命令将 flight 记录的内容复制到文件中。例如:参数 | 描述 | 数据类型 | 默认值 |
name | 记录的名称 | 字符串 | - |
filename | 将记录数据复制到文件中 | 字符串 | - |
maxAge | 转储文件的最大持续时间 | Time | 0s |
maxsize | 转储的最大字节数 | Long | 0 |
begin | 开始转储数据的时间 | 字符串 | - |
end | 转储数据的结束时间 | 字符串 | - |
path-to-gc-roots | 收集垃圾收集器 root 的路径 | 布尔值 | false |
3.5 配置JFR
使用
JFR.configure
诊断命令配置 flight 记录。例如:参数 | 描述 | 数据类型 | 默认值 |
repositorypath | 到仓库的路径 | 字符串 | - |
dumppath | 转储的路径 | 字符串 | - |
stackdepth | 堆栈深度 | Jlong | 64 |
globalbuffercount | 全局缓冲区数 | Jlong | 32 |
globalbuffersize | 全局缓冲区的大小 | Jlong | 524288 |
thread_buffer_size | 线程缓冲的大小 | Jlong | 8192 |
memorysize | 总内存大小 | Jlong | 16777216 |
maxchunksize | 单个磁盘块的大小 | Jlong | 12582912 |
Samplethreads | 激活线程抽样 | 布尔值 | true |
4、JMC使用介绍
4.1 JMC概览
启动测试代码后,启动 JMC 就可以在左侧的 JMV 浏览器中看到当前机器所有的 JVM 进程,选择测试代码进行通过 MBean 服务器进行连接,然后可以看到概览界面。

通过概览界面,可以看到进程的 JVM 内存使用情况,JVM CPU 使用情况,可以对进程的总体情况有一个初步的判断。
4.2 JMC触发器
JMC 触发器可以理解为一个 JVM 告警,可以对 JVM 进程的各种指标设置阈值,在超过指定阈值后可以收到告警提示,对进程异常情况监控十分方便。

这里对 CPU 占用率和死锁线程数进行监控,由于问题代码的原因,很快就触发了告警。

通过弹窗告警可以看到告警原因,以及对应的类信息,但是这里目前还看不到告警的具体原因,也就是说比如 CPU 占用率过高,并不知道为什么 CPU 会使用过高。
4.3 JMC分析内存
通过内存页面可以看到堆内存分配情况,由于问题代码中再不断地分配 BigDecimal 对象,这里也可以看出 BigDecimal 对象占用了最多的内存。对分析内存过高情况有一定的帮助。

如果想进一步分析内存占用来源,可以到线程页面,通过勾选分配复选框,可以看到哪个线程占用的内存最多,还可以看到线程的具体的调用堆栈。

4.4 JMC分析 CPU
线程页面勾选 CPU 概要分析可以查看占用 CPU 最高的线程,这里线程
cpu_high_thread
在不断地进行浮点计算,占用了较多的 CPU。
4.5 JMC分析死锁
线程页面勾选死锁检测可以直接看到死锁线程信息,并且有具体的线程堆栈。

另外一种检查死锁的方式是直接打印线程信息,在线程信息的最后部分,会输出死锁线程信息。使用 JMC 可以通过诊断命令中的
Thread.print
命令来实现这个功能。
5、实战
介绍完命令行的使用方式以及参数详解,现在我们就来进行实战使用JFR。这里有一个注意点就是,虽然JFR被设计为在JVM和应用程序的性能的影响会小,但是最好设置持续时间(duration),缓冲区数据的最长使用期限(maxage),限制收集的最大数据量(maxsize)。
首先定义个一个内存泄露的主程序,具体代码如下:
使用启动命令:
当项目启动完成,并发生 OutOfMemoryError 异常后,可以在目录下会发现一个名为flight.jfr的文件,将该文件拖至JDK Mission Control中,会自动进行分析。下图展示JDK Mission Control分析结果:

现在我们来具体分析这张图的结果:
- 图片右侧红色的分数表示,该次分析的重点关注的部分,展开后都会进行详细的描述造成原因,例如第一点
Application Halts
表示由于GC造成应用程序停顿时间过长,比例偏高。如果实在不想看,复制贴贴放进翻译软件中,也会明白大致的意思。
- 图片的左侧的侧边栏表示每个分析块的详细记录,有内存,有GC,有I/O等等,有红色小感叹号的是重点需要查看的部分。例如我们查看内存这一块

从图中可以得知,应用程序内存的使用,在5s内迅速跑满,然后触发GC操作,是什么方法导致内存使用如此迅速呢?根据
Method Profiling
的分析结果,可以看出时ArrayList的拷贝操作导致,如下图:
由以上操作我们可以快速定位到内存溢出的方法块,其他块的使用大家可以自行进行分析的时候查看,都是可视化的界面非常方便。使用JFR的时候最后不要定义太大的时间块,或者需要切分小块的时间块进行分析,因为大的时间块,会导致JFR文件十分巨大,本地进行分析的时候,也会产生卡顿或者电脑内存不够,导致本地机子卡死,无法分析。
- Author:mcbilla
- URL:http://mcbilla.com/article/1ed85c7d-7c1d-80f6-945b-dc28b29d354e
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts