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性能分析指南。

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#开发——ConcurrentDictionary集合

ConcurrentDictionary<TKey, TValue> 是 C# 中一个专为多线程场景设计的线程安全字典集合&#xff0c;位于 System.Collections.Concurrent 命名空间中。它允许多个线程同时对字典进行读写操作&#xff0c;而无需额外的同步措施。 一、集合特征 此集合有如下特征…

Unity百游修炼(2)——Brick_Breaker详细制作全流程

一、项目简介 Brick Breaker 是一款经典的打砖块游戏&#xff0c;本次案例将使用 Unity 引擎来实现该游戏的核心功能。 游戏画面如下&#xff1a; Brick_ breaker 二、项目结构概览和前期准备 &#xff08;1&#xff09;在 Unity 项目视图中&#xff0c;我们可以看到几个重要…

KubeSphere平台安装

KubeSphere简介 KubeSphere 是一款功能强大的容器管理平台&#xff0c;以下是其简介&#xff1a; 1&#xff09;基本信息 开源项目&#xff1a;基于 Apache-2.0 授权协议开源&#xff0c;由 Google Go、Groovy、HTML/CSS 和 Shell 等多种编程语言开发。基础架构&#xff1a;…

UE5销毁Actor,移动Actor,简单的空气墙的制作

1.销毁Actor 1.Actor中存在Destory()函数和Destoryed()函数 Destory()函数是成员函数&#xff0c;它会立即标记 Actor 为销毁状态&#xff0c;并且会从场景中移除该 Actor。它会触发生命周期中的销毁过程&#xff0c;调用 Destroy() 后&#xff0c;Actor 立即进入销毁过程。具体…

Hadoop 基础原理

Hadoop 基础原理 基本介绍Hadoop 的必要性Hadoop 核心组件Hadoop 生态系统中的附加组件 HDFSHDFS 集群架构HDFS 读写流程HDFS 写流程HDFS 读流程 NameNode 持久化机制 MapReduce底层原理示例 Hadoop 是一个由 Apache 基金会开发的分布式系统基础架构&#xff0c;主要解决海量数…

Linux编辑器

1.三种模式 2.图例 3.wq 4.光标的使用

2.24DFS和BFS刷题

洛谷P2895&#xff1a;用BFS走出危险区域&#xff0c;危险区域存在时间&#xff0c;我们用ma记录最快变成危险区域的时间&#xff0c; 然后每次枚举时间1然后跟ma数组比较看能不能走&#xff0c;然后时间复杂度为O(305^2)。 #include<iostream> #include<cstring>…

TMDS视频编解码算法

因为使用的是DDR进行传输&#xff0c;即双倍频率采样&#xff0c;故时钟只用是并行数据数据的5倍&#xff0c;而不是10倍。 TMDS算法流程&#xff1a; 视频编码TMDS算法流程实现&#xff1a; timescale 1 ps / 1ps //DVI编码通常用于视频传输&#xff0c;将并行数据转换为适合…

C++中tuple的用法

C中tuple的用法 在C中&#xff0c;std::tuple 是一个模板类&#xff0c;用于存储一组不同类型的值。它类似于 Python 中的元组&#xff0c;但具有更强大的功能&#xff0c;例如支持不同类型的元素和更复杂的操作。std::tuple 是 C11 标准引入的&#xff0c;位于 <tuple>…

计算机网络————(一)HTTP讲解

基础内容分类 从TCP/IP协议栈为依托&#xff0c;由上至下、从应用层到基础设施介绍协议。 1.应用层&#xff1a; HTTP/1.1 Websocket HTTP/2.0 2.应用层的安全基础设施 LTS/SSL 3.传输层 TCP 4.网络层及数据链路层 IP层和以太网 HTTP协议 网络页面形成基本 流程&#xff1a…

【网络编程】广播和组播

数据包发送方式只有一个接受方&#xff0c;称为单播。如果同时发给局域网中的所有主机&#xff0c;称为广播。只有用户数据报(使用UDP协议)套接字才能广播&#xff1a; 广播地址以192.168.1.0 (255.255.255.0) 网段为例&#xff0c;最大的主机地址192.168.1.255代表该网段的广…

小程序如何实现跨页面通信

前言 最近有很多同学问&#xff0c;小程序里面如何进行跨页面通信。看了下之前的老代码&#xff0c;基本都是基于onShow或者localStorage。虽然可以实现&#xff0c;但是并不怎么优雅。 今天就来聊一聊&#xff0c;小程序的跨页面通信的几种实现方案。或许会有你想要的方案&a…

【工具】win-画图 保留图片信息并仅改变图片比例的工具

Windows 系统自带的“画图”工具 Windows 系统自带的“画图”&#xff08;Paint&#xff09;工具可以进行简单的图片编辑&#xff0c;包括调整图片大小和比例。 使用方法&#xff1a; 打开“画图”工具&#xff08;可以通过在开始菜单中搜索“画图”或“Paint”&#xff09;。…

如何编辑autodl中以.bashrc结尾的隐藏文件

在nnunet的运行过程中遇到了设置环境变量的问题。之前没有接触过linux系统&#xff0c;但是autodl里面默认是linux系统。.bashrc 是一个在 Bash shell 启动时执行的脚本文件&#xff0c;常用于设置环境变量、定义别名、加载函数等&#xff0c;用户可以通过编辑这个文件来定制自…

实验3 知识表示与推理

实验3 知识表示与推理 一、实验目的 &#xff08;1&#xff09;掌握知识和知识表示的基本概念&#xff0c;理解其在AI中的深刻含义与意义&#xff1b; &#xff08;2&#xff09;熟悉AI中常用的知识表示方法的优缺点及其应用场景&#xff1b; &#xff08;3&#xff09;掌握产…

在 M1 Mac 上解锁 TensorFlow GPU 加速:从环境搭建到实战验证

在 M1 Mac 上解锁 TensorFlow GPU 加速&#xff1a;从环境搭建到实战验证 前言&#xff1a;苹果芯片的深度学习新纪元 随着 Apple Silicon 芯片的普及&#xff0c;M1/M2/M3 系列 Mac 已成为移动端深度学习开发的新选择。本文将以 TensorFlow 2.x 为例&#xff0c;手把手教你如…

Python 数据分析概述 ①

一文读懂Python数据分析&#xff1a;从基础到实践全攻略 在当今数字化浪潮中&#xff0c;数据分析已然成为解锁海量数据价值的关键钥匙&#xff0c;而Python凭借其独特优势&#xff0c;在数据分析领域大放异彩。今天&#xff0c;咱们就结合教学PPT内容&#xff0c;深入探索Pyt…

【Gin-Web】Bluebell社区项目梳理6:限流策略-漏桶与令牌桶

本文目录 一、限流二、漏桶三、令牌桶算法四、Gin框架中实现令牌桶限流 一、限流 限流又称为流量控制&#xff0c;也就是流控&#xff0c;通常是指限制到达系统的并发请求数。 限流虽然会影响部分用户的使用体验&#xff0c;但是能一定程度上保证系统的稳定性&#xff0c;不至…

Linux高并发服务器开发 第十九天(线程 进程)

目录 1.进程组和会话 2.守护进程 2.1守护进程daemon概念 2.2创建守护进程 3.线程 3.1线程的概念 3.2线程内核三级映射 3.3线程共享 3.4线程优缺点 4.线程控制原语 4.1获取线程id 4.2创建线程 4.3循环创建N个子线 4.4子线程传参地址&#xff0c;错误示例 4.5线程…

软件工程和系统分析与设计

软件工程 1、软件危机 2、软件过程模型 2.1 瀑布模型 2.2原型模型 2.3螺旋模型 2.4敏捷模型 2.5软件统一过程 3、软件能力成熟度模型 CMM 4、软件能力成熟度模型集成 CMMI 系统分析与设计 1、结构化方法SASD 1.1结构化分析 DFD 1.2结构化设计 SD-是一种面向数据流的设计…