---
title: Java Flight Recorder - 事件机制详解-官方技术文章-鲲鹏社区
description: 本文介绍 JFR 剖析的事件机制，希望能帮助大家从原理上理解 JFR 并正确使用 JFR。
keywords: Java,Flight,Recorder,事件机制详解,官方技术文章,鲲鹏社区,Prologue,对于
url: https://www.hikunpeng.com/developer/techArticles/20230815-4?envFlag=1
section: (其他)
---

# Java Flight Recorder - 事件机制详解-官方技术文章-鲲鹏社区

URL: https://www.hikunpeng.com/developer/techArticles/20230815-4?envFlag=1
描述: 本文介绍 JFR 剖析的事件机制，希望能帮助大家从原理上理解 JFR 并正确使用 JFR。
关键词: Java,Flight,Recorder,事件机制详解,官方技术文章,鲲鹏社区,Prologue,对于

官方技术文章 [了解详情](https://www.hikunpeng.com/zh/developer/techArticles)

Java Flight Recorder - 事件机制详解

Java Flight Recorder - 事件机制详解

实战经验

发表于 2022/01/06

0

1. 本篇文章中的源码大部分来自 openjdk8u262

2. 本文出发点是梳理 JFR 的事件机制, 侧重点在于理解而非应用

3. 使用上可以不要太过拘泥细节，可以具体问题具体分析。

## Prologue

### 1. 对于 JFR 我们有着怎样的预期

它是一个辅助分析工具，我们希望借助它，尽可能低开销地收集运行时数据，从而辅助对 JVM 可能存在的故障、性能瓶颈进行分析。

我们结合 JFR 的 Goals 来看：

（1）提供基于生成和消费数据作为事件的 API

（2）提供缓存机制和二进制数据格式

（3）允许配置和过滤事件

（4）为 OS、JVM、JDK 库提供相应的事件

从中，我们能粗略地获取这些信息 ：

（1）事件以自描述的二进制形式 (.jfr) 被保存着

（2）事件中包含了数据，事件 ≈ 数据

（3）.jfr 文件 => read by some Provided API => 重现运行时数据 [=> 可视化 ]

我们想尝试了解 JFR 的事件驱动机制，具体点就是回答几个问题：

一个事件何时产生 / 启动监控? 经历了怎样的路径? 如何被保存? 保存到哪里?

### 2. JFR 是事件驱动的 (JEP 328)

本节主要是一些前置信息 （假如你有所了解，可以快速浏览或者跳过本节内容）：JVM 行为基本都是 Event，如类加载对应着 Class Load Event, 垃圾回收对应 GC Event；Event 主要由 timestamp, event name, additional info, data 这几部分组成。Event 收集四类事件的信息：

（1）Instant Event，发生就收集（e.g. Thread Start ...）

（2）Duration Event，持续收集一段时间（e.g. GC Event ...）

（3）Timed Event，收集超过指定时间的事件

（4）Sample Event，按频率采样

以 JFR 的 Class Load Event 为例, 看看一个事件的结构。（共计 24 bytes）

```
<memory address> : 98 80 80 00 87 02 95 ae e4 b2 92 03 a2 f7 ae 9a 94 02 02 01 8d 11 00 00

- Event Size : 98 80 80 00
- Event ID : 87 02
- TimeStamp : 95 ae e4 b2 92 03
- Duration : a2 f7 ae 9a 94 02
- Thread ID : 02
- Stack trace ID : 01
- PayLoad（记录的数据，fields 取决于各个 Event 类型）：
  - 加载的类 : 8d 11
  - 定义类的 ClassLoader : 00
  - 初始化类的 ClassLoader : 00
```

多个线程都会产生 Event，线程通过无锁（Lock-free）设计记录事件。线程将事件首先写入到 ThreadLocalBuffer（简称 TLB）, TLB 被填满后，将被转存到 Global buffer（circular），对于较旧的数据，可以通过配置，选择丢弃或者写入磁盘，以便连续保存。示意图如下所示：

注意：TLB、Global Buffer 和磁盘文件中的事件记录不会相互备份，未及时转存的数据可能发生丢失，本文不会就这点展开阐述。

前置内容已经交代清楚，接着回到正轨。

## 一个事件的生命周期

以下是枯燥乏味的一堆代码，但是不得不看。首先来看 JFR 的结构，如下图所示：

肉眼可见的一堆钩子，这些 hook 用于记录对应的触发事件。

我们简单地挑一个 Thread Start 的事件，关注一下它的整个被触发到被记录的过程。在线程创建并执行时会调用记录 JFR 事件，代码如下：

可见当一个新的 Java 线程被创建时，只要开启了 JFR, 那么就会执行上述代码;

接着看一下 on_thread_start 干了什么：

在此，我们看到了一个事件 EventThreadStart，并且在事件中设置信息后被提交。

在 JEP 328 中有一个更为简单直接例子，如下：

无需太过关心其内容。我们只需关注这个事件生成的结构：

这里的 EventType 定义于 jfrEventClass.hpp, 该文件是编译时生成的，简单贴一下生成逻辑，可以参考 Makefile 文件，如下 （同样无需在意太多细节）:

回到主旋律，继续来看事件的结构和成员函数，如下：

其中最为重要的成员函数是 JfrEvent::commit 方法，用于提交事件，代码如下：

在函数中，最后一段代码, 也是核心所在，用于真正记录事件：

这下，就可以很容易地和 第 1 节 的内容对应上了，特别是其中的事件模型的图片：

## Epilogue

用户是否可以自定义一个 JFR 事件？注意点有哪些？

这里通过 JEP 328 里的例子（稍微有点改动），来展示如何自定义 JFR 事件。

通过编译后直接执行如下命令：

```
$> java -XX:StartFlightRecording,filename=event.jfr Test
```

可以得到如下日志信息：

```
Started recording 1. No limit specified, using maxsize=250MB as default.
Use jcmd 57980 JFR.dump name=1 to copy recording data to file.
```

日志可以通过标准的 API 进行解析，下面通过一个简单代码解析上面生成的事件，代码如下：

编译运行

```
$> java Viewer | less
```

可以得到如下结果。

相信此时你已经对 JFR 的事件机制有了个不错的感觉。

实际上 JFR 的使用一般配合 JMC[1] 使用，在 JMC 中通过页面可以得到统计信息，更有助于判断系统的运行情况。

## 参考

[1]https://adoptopenjdk.net/jmc.html [了解详情](https://adoptopenjdk.net/jmc.html)

编者按：Java Flight Recorder（简称为 JFR）曾经是 Oracle JDK 商业版的附属组件，在 JDK 11 中被正式开始开源，后又被移植到 JDK8 中。JFR 本身对运行期系统的侵入性很小，同时又能提供相对准确和丰富的运行期信息；合理使用该工具可以极大地提高工作效率。本文介绍 JFR 剖析的事件机制，希望能帮助大家从原理上理解 JFR 并正确使用 JFR。

本页内容
