JVM生产环境问题定位与解决实战(三):揭秘Java飞行记录器(JFR)的强大功能

提到飞行记录器,或许你的脑海中并未立刻浮现出清晰的画面,但一说起“黑匣子”,想必大多数人都能恍然大悟,知晓其重要性及用途。在航空领域,黑匣子作为不可或缺的设备,默默记录着飞行过程中的每一项关键数据,从飞行高度、速度到机舱内的对话,无一遗漏。一旦飞机发生事故,这些珍贵的数据便成为调查人员还原事件真相、精准定位事故原因的宝贵线索。

同样地,在软件开发的世界里,也有这样一个“黑匣子”般的存在——Java飞行记录器(Java Flight Recorder,简称JFR)。它借鉴了航空黑匣子的设计理念,却应用于一个截然不同的领域:Java应用程序的性能分析与故障排查。

一、 背景与概述

1.1 JFR 简介

Java Flight Recorder (JFR) 是 Oracle JDK 内置的性能分析工具,用于监控 JVM 和 Java 应用程序的运行时行为。其特点包括:

  • 低开销:生产环境中通常仅产生 1% 左右的性能损耗
  • 实时监控:可记录 JVM 事件、方法调用、内存分配等详细信息、支持动态启停记录
  • 事件驱动:捕获超过 200 种不同类型的事件(JDK 11+)
  • 集成分析:与 Java Mission Control (JMC) 工具深度集成
    Java飞行记录器(Java Flight Recorder,JFR)是JVM内置的低开销性能分析和故障排查工具,用于记录Java应用程序运行时的详细数据,类似飞机的“黑匣子”。它通过事件机制采集JVM和应用程序的运行时信息,包括CPU、内存、线程、垃圾回收(GC)、锁竞争等数据,帮助开发者定位性能瓶颈和异常问题。

📌 适用场景:性能调优、内存泄漏排查、高 CPU 使用率分析等

1.2. JFR 的发展历史

1.2.1. 关键里程碑
  • JRockit Flight Recorder:JFR 最初是由 BEA Systems 开发的 JRockit JVM的一部分,当时被称为 JRockit Flight Recorder。
  • Oracle 收购 Sun Microsystems:随着 Oracle 收购了 Sun Microsystems(Java的原始开发者)以及 BEA Systems,JFR 被集成到了 HotSpot JVM 中,并从 JDK 7u40 和 JDK 8u40开始被包含进去。此时,JFR 还是一个商业特性,需要许可证才能在生产环境中使用。
  • JDK 11:JFR 2.0版本发布,提供了更丰富的功能集。JFR 成为了 OpenJDK 的一部分,无需额外安装。作为一个开源项目,不再需要商业许可证即可使用。
  • JDK 14:引入了 JFR Event Streaming,允许实时处理 JFR 事件。
  • JDK 17:稳定支持虚拟线程事件,并增强云原生环境兼容性。
1.2.2. Java Flight Recorder (JFR) 版本变化
JDK版本发布日期主要变化与新特性
JDK 7u402013年9月- JFR首次在Oracle JDK中作为商业特性引入
- 提供基本的事件记录功能,如GC、线程等
- JCMD控制
JDK 8u402015年3月- 增加了更多的内置事件类型
- 改善了性能开销,减少了对应用程序的影响
-JMX动态控制
JDK 92017年9月- 开始支持自定义事件
- 引入了更丰富的API用于编程控制JFR会话
JDK 112018年9月- JFR成为OpenJDK的一部分(JEP 328)
- JFR 2.0 版本、完全开源,社区可以贡献代码
- 支持JIT编译器事件,记录编译器活动和性能数据
- 对容器环境的支持,更好地适应Docker等容器化部署
- 改进了数据格式和压缩算法,提高了存储效率
JDK 132019年9月- 引入了jdk.jfr.consumer模块,允许程序化地读取和分析JFR文件
- 增强了事件过滤和配置选项
JDK 142020年3月- 添加了对虚拟线程的支持(实验性)
- 改进了事件元数据的可读性和灵活性
JDK 152020年9月- 引入了新的事件类别,如jdk.VirtualThread*
- 改进了JFR与容器环境的兼容性
JDK 162021年3月- 支持动态调整采样率
- 增强了对云原生环境的支持
JDK 172021年9月- 稳定版支持虚拟线程事件
- 改进了与Kubernetes等平台的集成
- 增强了安全性,包括对敏感数据的保护
JDK 182022年3月- 引入了对GraalVM Native Image的支持
- 改进了内存管理和垃圾回收事件的详细程度
JDK 192022年9月- 增强了对多租户环境的支持
- 改进了事件的时间戳精度
JDK 202023年3月- 引入了新的事件类型,如jdk.CodeCacheFlush
- 改进了对异步事件的支持
JDK 212023年9月- 进一步增强了对虚拟线程的支持
- 改进了与Spring Boot等框架的集成

二、启用 JFR

2.1. 通过命令行参数启动

# JDK 8 需要添加以下参数,解锁商业功能
java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder ...

# JDK 11+ 开源版本直接启用
java -XX:StartFlightRecording:delay=5s,duration=60s,name=MyRecording,filename=recording.jfr ...

常用选项:

  • filename=recording.jfr:指定输出文件名
  • duration=60s:设置录制时长
  • delay=10s:延迟启动时间
  • settings=profile:使用预定义配置文件(如profile, default)
  • name=MyRecording:为录制会话命名

2.2. 运行时触发,使用jcmd工具

jcmd <PID> JFR.start duration=60s filename=recording.jfr
jcmd <PID> JFR.dump filename=partial.jfr
jcmd <PID> JFR.stop

2.3. 通过JMC(JDK Mission Control)启动

操作步骤:

  1. 打开JMC工具。
  2. 连接到正在运行的JVM实例。
  3. 在左侧导航栏选择“Flight Recorder”。
  4. 配置录制设置(如持续时间、事件类型等)。
  5. 点击“Start Recording”。

2.4 通过Spring Boot Actuator集成启动

2.4.1. 添加依赖

pom.xml中添加依赖:

<dependency> 
	<groupId>org.springframework.boot</groupId> 
	<artifactId>spring-boot-actuator</artifactId> 
</dependency>
2.4.2. 使用HTTP接口启动
curl -X POST http://localhost:8080/actuator/jfr/start 
curl -X POST http://localhost:8080/actuator/jfr/dump?filename=myapp_recording.jfr 
curl -X POST http://localhost:8080/actuator/jfr/stop

三、JFR 事件

在 JFR中,一切皆为 Event!JFR 事件是 JFR 捕获和记录的最小数据单元,每个事件都代表了在特定时间点上系统或应用某个方面的信息。每条事件记录包含了多个数据字段,如时间戳、事件持续时间、以及其他与业务或系统状态相关的元数据。大部分的 Event,都有 Event 是在哪个线程发生的,Event 发生的时候这个线程的调用栈,Event 的持续时间。这就非常有用了,利用这些信息,我们可以回溯 Event 发生当时的情况。

3.1. 按来源分类

类别说明示例事件
JVM 事件JVM 内部操作产生的事件jdk.GarbageCollection, jdk.JITCompilation
JDK 库事件JDK 类库(如 I/O、网络、集合)触发的事件jdk.FileRead, jdk.SocketWrite
OS 事件操作系统级别的资源监控数据jdk.CPULoad, jdk.PhysicalMemory
自定义事件开发者定义的应用层事件com.example.OrderProcessEvent

3.2. 按触发方式分类

  • 阈值触发事件:当指标超过预设阈值时记录(如 jdk.CPULoad
  • 周期采样事件:按固定时间间隔采集(如 jdk.ThreadAllocationStatistics
  • 即时触发事件:特定操作发生时立即记录(如 jdk.ExceptionThrown

3.3、关键内置事件

3.3.1. 垃圾回收相关
事件名称作用
jdk.GarbageCollection记录 GC 暂停时间和原因(Young GC/Full GC)
jdk.OldObjectSample跟踪可能引发内存泄漏的旧对象(需启用 -XX:StartFlightRecording=old-object-queue-size=256
3.3.2. 线程与锁
事件名称作用
jdk.ThreadSleep记录线程睡眠时间
jdk.JavaMonitorWait监控 synchronized 锁等待时间
jdk.ThreadPark跟踪 LockSupport.park() 调用(如 AQS 锁)
3.3.3. 异常与错误
事件名称作用
jdk.ExceptionThrown记录所有异常抛出事件(包括堆栈跟踪)
jdk.ErrorThrown记录严重错误事件(如 OutOfMemoryError)
3.3.4. I/O 与网络
事件名称作用
jdk.FileRead跟踪文件读取操作(包含路径和耗时)
jdk.SocketRead记录 Socket 读取数据量及延迟
3.3.5. JIT 与类加载
事件名称作用
jdk.JITCompilation记录方法编译耗时和优化级别
jdk.ClassLoad跟踪类加载过程及耗时

3.4. 多线程低开销设计与异步存储原理

3.4.1. 事件产生与多线程协作
  • 多线程事件触发
    JFR 事件由业务线程在执行过程中主动生成(如方法调用、异常抛出、锁竞争等),每个线程独立维护线程本地缓冲区(Thread Local Buffer),直接以二进制格式缓存事件流。
  • 线程隔离与无锁设计
    各线程仅操作自身缓冲区,避免全局锁竞争,确保事件记录低延迟(通常低于微秒级)。
3.4.2. 分级缓冲存储流程
  1. 线程本地缓冲区(Thread Local Buffer)

    • 默认容量约 34KB,采用循环队列结构存储二进制事件数据。
    • 缓冲区满时触发异步刷写(非阻塞),将数据批量推送至全局缓冲区。
  2. 全局缓冲区(Global Buffer)与持久化

    • 全局缓冲区整合多线程事件流,由独立的 jfr 守护线程负责管理。
    • 后台线程将全局缓冲区的数据异步写入磁盘(生成 .jfr 文件),完全分离业务线程与I/O操作,避免阻塞主逻辑。
3.4.3. 高效性核心设计
  • 二进制直接存储
    事件以二进制格式在内存中流转,跳过多余的序列化/反序列化步骤,减少 CPU 开销。
  • 异步分层处理
    业务线程仅负责生成事件,缓冲区刷写与持久化由独立线程完成,通过批量合并降低 I/O 频率。
  • 动态采样与可控开销
    JFR 支持按需配置采样率(如 -XX:FlightRecorderOptions=stackdepth=128),通过 JVM 内部 Hook 实现非侵入式监控,无需代码插桩。
3.4.4. 自定义事件对业务的影响
  • 轻量级托管机制
    开发者通过继承 jdk.jfr.Event 定义的事件,由 JFR 框架统一管理。调用 commit() 方法时,事件仅写入线程本地缓冲区,不占用业务线程额外时间
  • 背压(Backpressure)保护
    若事件产生速率超过持久化能力(如高频自定义事件),JFR 会触发背压机制,选择性丢弃低优先级事件或限制事件生成,防止资源耗尽。
  • 可控的性能损耗
    合理使用自定义事件(如避免每秒超 10 万次高频提交),对业务延迟的影响通常可控制在 1%~2% 以内,极端场景需结合采样策略优化。

四、自定义事件开发

4.1. 自定义事件开发价值

通过自定义JFR事件,开发者可以:

  1. 业务指标可视化:记录订单创建、支付处理等关键业务指标
  2. 精准性能分析:追踪特定方法执行耗时、资源消耗
  3. 上下文关联:将JVM事件与业务逻辑关联分析
  4. 生产环境诊断:低开销实时监控生产系统

4.2. 自定义事件开发四部曲

4.2.1. 定义事件类
@Name("com.example.OrderCreated") 
@Label("Order Created Event")
@Category("Business")
@Description("Records order creation information")
public class OrderCreatedEvent extends Event {
    
    @Label("Order ID")
    public String orderId;

    @Label("Total Amount")
    @Amount(Currency.CNY)
    public double amount;

    @Label("User Type")
    public UserType userType;

    @Label("Creation Duration")
    @Timespan(Timespan.MILLISECONDS)
    public long duration;
}

注解说明:

  • @Name: 定义事件的全局唯一标识符,推荐使用反向域名命名法(类似Java包名),避免与JVM内置事件冲突。
  • @Label: 事件名称,在JMC等可视化工具中作为显示名称。
  • @Category: JMC中的分类显示,在JMC左侧导航树状呈现。
  • @Description: 提供事件的详细文档说明吗,在JMC中通过"Show Description"查看。
4.2.2. 触发事件记录
public class OrderService {
    
    public void createOrder(OrderRequest request) {
        OrderCreatedEvent event = new OrderCreatedEvent();
        if (event.isEnabled()) {
            long start = System.nanoTime();
            
            // 业务逻辑执行...
            
            event.orderId = generatedId;
            event.amount = calculateAmount();
            event.userType = getCurrentUserType();
            event.duration = (System.nanoTime() - start) / 1_000_000;
            event.commit();
        }
    }
}
4.2.2. 事件注册与启用
@Registered(true)
public class OrderCreatedEvent extends Event {
    //...
}

动态启用配置:

jcmd <PID> JFR.configure  threshold=10ms  stacktrace=true  path-to-gc-roots=true
4.2.4. 事件数据分析

使用JMC分析:

public static void analyzeJfr(File recordingFile) throws IOException {
    try (RecordingStream rs = new RecordingStream(recordingFile)) {
        rs.onEvent("com.example.OrderCreated", event -> {
            System.out.printf("订单%s 金额%.2f 耗时%dms%n",
                event.getString("orderId"),
                event.getDouble("amount"),
                event.getLong("duration"));
        });
        rs.start();
    }
}

五、高级技巧

5.1 异步事件处理

@Async
public class AsyncPaymentEvent extends Event {
    //...
}

5.2 阈值控制

@Threshold("20 ms")
public class SlowMethodEvent extends Event {
    //...
}

5.3 自定义转换器

public class UserTypeConverter extends Converter<UserType> {
    public String toString(UserType type) {
        return type.name().toLowerCase();
    }
}

public class OrderCreatedEvent extends Event {
    @Converter(UserTypeConverter.class)
    public UserType userType;
}

六、总结

通过自定义JFR事件,开发者可以获得:

  • 生产环境细粒度监控能力
  • 业务与JVM事件的关联分析
  • 传统日志无法实现的性能洞察

建议结合JMC可视化工具和jfr命令行工具,构建完整的性能监控体系。下一篇来具体介绍使用JMC进行JFR性能分析指南。

最佳实践:从关键业务路径开始逐步增加事件埋点,通过持续的性能分析迭代优化事件配置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/977898.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python修仙编程】(二) Python3灵源初探(2)

第一部分&#xff1a;林羽的修仙之旅——字符串与布尔类型的修炼 林羽站在练气期一阶的起点&#xff0c;望着手中的《Python无极心法》秘籍&#xff0c;心中充满了期待。师傅玄天真人在一旁微笑着说道&#xff1a;“林羽&#xff0c;今天我们要修炼的是‘字符串’和‘布尔类型…

【HTML— 快速入门】HTML 基础

准备工作 vscode下载 百度网盘 Subline Text 下载 Sublime Text下载 百度网盘 vscode 下载 Sublime Text 是一款轻量好用的文本编辑器&#xff0c;我们在写前端代码时&#xff0c;使用 Sublime Text 打开比使用记事本打开&#xff0c;得到的代码体验更好&#xff0c;比 vscode…

pipeline 使用git parameter插件实现动态选择分支构造

效果&#xff0c;&#xff0c;点击build with Parameters 就会出现右边的当前仓库的所有的分支&#xff0c;默认最多显示5个&#xff0c;可以修改配置&#xff0c;修改显示的最大分支数量。如果分支太多&#xff0c;可以通过右边的过滤框输入过滤。 安装git params插件 搜索g…

国产OS上完整编译Qt5.15、搭建基本开发环境需要的库

近期有师弟问我国产OS安装Qt5.15编译老是不完整&#xff0c;不是没声音&#xff0c;就是没视频&#xff0c;或者没有xcb。通过QEMU模拟Arm64&#xff0c;闲来20几天摸索&#xff0c;完整编译了Qt5.15&#xff0c;并编译成功了我的SDR玩具taskBus。 1.主要结论&#xff1a; 该O…

数据库 安装initializing database不通过

出现一下情况时&#xff1a; 处理方法&#xff1a; 将自己的电脑名称 中文改成英文 即可通过

【视频2 - 4】初识操作系统,Linux,虚拟机

&#x1f4dd;前言说明&#xff1a; ●本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;主要跟随B站博主灵茶山的视频进行学习&#xff0c;专栏中的每一篇文章对应B站博主灵茶山的一个视频 ●题目主要为B站视频内涉及的题目以及B站视频中提到的“课后作业”。…

AI绘画软件Stable Diffusion详解教程(2):Windows系统本地化部署操作方法(专业版)

一、事前准备 1、一台配置不错的电脑&#xff0c;英伟达显卡&#xff0c;20系列起步&#xff0c;建议显存6G起步&#xff0c;安装win10或以上版本&#xff0c;我的显卡是40系列&#xff0c;16G显存&#xff0c;所以跑大部分的模型都比较快&#xff1b; 2、科学上网&#xff0…

将Ubuntu操作系统的安装源设置为阿里云

在使用Ubuntu操作系统时,默认的软件源通常是国外的仓库,这可能会导致软件安装和更新速度较慢。为了提高下载速度和稳定性,我们可以将Ubuntu的安装源设置为阿里云镜像源。以下是详细步骤: 一、准备工作 在开始之前,请确保您的Ubuntu系统可以正常上网,并且您拥有管理员权…

IP-------GRE和MGRE

4.GRE和MGRE 1.应用场景 现实场景 居家工作&#xff0c;公司工作&#xff0c;分公司工作----------需要传输交换数据--------NAT---在该场景中需要两次NAT&#xff08;不安全&#xff09; 为了安全有两种手段-----1.物理专线---成本高 2.VPN--虚拟专用网---隧道技术--封装技…

Visual Studio Code 跨平台安装与配置指南(附官方下载链接)

一、软件定位与核心功能 Visual Studio Code&#xff08;简称VS Code&#xff09;是微软开发的开源跨平台代码编辑器&#xff0c;支持超过50种编程语言的智能补全、调试和版本控制功能。2025版本新增AI辅助编程模块&#xff0c;可自动生成单元测试代码和API文档注释。 二、下载…

小智AI桌宠机器狗

本文主要介绍如何利用开源小智AI制作桌宠机器狗 1 源码下载 首先下载小智源码,下载地址, 下载源码后,使用vsCode打开,需要在vscode上安装esp-idf,安装方式请自己解决 2 源码修改 2.1添加机器狗控制代码 在目录main/iot/things下添加dog.cc文件,内容如下; #include…

Linux:进程信号(二.信号的保存与处理、递达、volatile关键字、SIGCHLD信号)

目录 1.信号保存 1.1递达、未决、阻塞等概念 1.2再次理解信号产生与保存 1.3信号集操作函数 sigset_t类型 sigemptyset() 函数 sigismember()函数 sigaddset ()函数 sigdelset() 函数 sigprocmask()系统调用 sigpending()系统调用 2.信号的处理/递达 2.1信号处理时…

kotlin 知识点 七 泛型的高级特性

对泛型进行实化 泛型实化这个功能对于绝大多数Java 程序员来讲是非常陌生的&#xff0c;因为Java 中完全没有这个概 念。而如果我们想要深刻地理解泛型实化&#xff0c;就要先解释一下Java 的泛型擦除机制才行。 在JDK 1.5之前&#xff0c;Java 是没有泛型功能的&#xff0c;…

go基本语法

跟Java比较学习。 hello word 示例代码 test1.go文件&#xff1a; // 包路径 package main// 导入模块&#xff0c;下面两种都行 import ("fmt" ) import "log"// main方法 func main() {log.Print("hello word !!!")fmt.Print("hello …

Linux内核,slub分配流程

我们根据上面的流程图&#xff0c;依次看下slub是如何分配的 首先从kmem_cache_cpu中分配&#xff0c;如果没有则从kmem_cache_cpu的partial链表分配&#xff0c;如果还没有则从kmem_cache_node中分配&#xff0c;如果kmem_cache_node中也没有&#xff0c;则需要向伙伴系统申请…

冯诺依曼体系结构 ──── linux第8课

目录 冯诺依曼体系结构 关于冯诺依曼&#xff0c;必须强调几点&#xff1a; 冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系 输入单元&#xff1a;包括键盘, 鼠标&#xff0c;网卡,扫…

国标28181协议在智联视频超融合平台中的接入方法

一. 国标28181介绍 国标 28181 协议全称是《安全防范视频监控联网系统信息传输、交换、控制技术要求》&#xff0c;是国内视频行业最重要的国家标准&#xff0c;目前有三个版本&#xff1a; 2011 年&#xff1a;推出 GB/T 28181-2011 版本&#xff0c;为安防行业的前端设备、平…

2024-2025 学年广东省职业院校技能大赛 “信息安全管理与评估”赛项 技能测试试卷(四)

2024-2025 学年广东省职业院校技能大赛 “信息安全管理与评估”赛项 技能测试试卷&#xff08;四&#xff09; 第一部分&#xff1a;网络平台搭建与设备安全防护任务书第二部分&#xff1a;网络安全事件响应、数字取证调查、应用程序安全任务书任务 1&#xff1a;应急响应&…

音乐游戏Dance Dance Revolution(DDR)模拟器

文章目录 &#xff08;一&#xff09;Dance Dance Revolution&#xff08;1.1&#xff09;基本情况&#xff08;1.2&#xff09;机体 &#xff08;二&#xff09;模拟器&#xff08;2.1&#xff09;主程序&#xff08;2.2&#xff09;模拟器主题 &#xff08;三&#xff09;曲谱…

货车一键启动无钥匙进入手机远程启动的正确使用方法

一、移动管家货车无钥匙进入系统的使用方法 基本原理&#xff1a;无钥匙进入系统通常采用RFID无线射频技术和车辆身份识别码识别系统。车钥匙需要随身携带&#xff0c;当车钥匙靠近货车时&#xff0c;它会自动与货车的解码器匹配。开门操作&#xff1a;当靠近货车后&#xff0…