JDK 14的新特性:JFR,JMC和JFR事件流

简介

Java Flight Recorder(JFR)是JVM的诊断和性能分析工具。它可以收集有关JVM以及在其上运行的Java应用程序的数据。JFR是集成到JVM中的,所以JFR对JVM的性能影响非常小,我们可以放心的使用它。

一般来说,在使用默认配置的时候,性能影响要小于1%。

JFR的历史很久远了。早在Oracle2008年收购BEA的时候就有了。JFR一般和JMC(Java Mission Control)协同工作。

JFR是一个基于事件的低开销的分析引擎,具有高性能的后端,可以以二进制格式编写事件,而JMC是一个GUI工具,用于检查JFR创建的数据文件。

这些工具最早是在BEA的JRockit JVM中出现的,最后被移植到了Oracle JDK。最开始JFR是商用版本,但是在JDK11的时候,JFR和JMC完全开源了,这意味着我们在非商用的情况下也可以使用了。

而在今天的JDK 14中,引入了一个新的JFR特性叫做JFR Event Streaming,我们将在本文中详细讲解。

先介绍一下JFR和JMC。

更多内容请访问www.flydean.com

JFR

上面我们简单的介绍了一下JFR。JFR是JVM的调优工具,通过不停的收集JVM和java应用程序中的各种事件,从而为后续的JMC分析提供数据。

Event是由三部分组成的:时间戳,事件名和数据。同时JFR也会处理三种类型的Event:持续一段时间的Event,立刻触发的Event和抽样的Event。

为了保证性能的最新影响,在使用JFR的时候,请选择你需要的事件类型。

JFR从JVM中搜集到Event之后,会将其写入一个小的thread-local缓存中,然后刷新到一个全局的内存缓存中,最后将缓存中的数据写到磁盘中去。

或者你可以配置JFR不写到磁盘中去,但是这样缓存中只会保存部分events的信息。这也是为什么会有JDK14 JEP 349的原因。

开启JFR有很多种方式,这里我们关注下面两种:

  1. 添加命令行参数
-XX:StartFlightRecording:<options>

启动命令行参数的格式如上所述。

JFR可以获取超过一百种不同类型的元数据。如果要我们一个个来指定这些元数据,将会是一个非常大的功能。所以JDK已经为我们提供了两个默认的profile:default.jfc and profile.jfc。

其中 default.jfc 是默认的记录等级,对JVM性能影响不大,适合普通的,大部分应用程序。而profile.jfc包含了更多的细节,对性能影响会更多一些。

如果你不想使用默认的两个jfc文件,也可以按照你自己的需要来创建。

下面看一个更加完整的命令行参数:

-XX:StartFlightRecording:disk=true,filename=/tmp/customer.jfr,maxage=5h,settings=profile

上面的命令会创建一个最大age是5h的profile信息文件。

  1. 使用jcmd

命令行添加参数还是太麻烦了,如果我们想动态添加JFR,则可以使用jcmd命令。

jcmd <pid> JFR.start name=custProfile settings=default
jcmd <pid> JFR.dump filename=custProfile.jfr
jcmd <pid> JFR.stop

上面的命令在一个运行中的JVM中启动了JFR,并将统计结果dump到了文件中。

上面的custProfile.jfr是一个二进制文件,为了对其进行分析,我们需要和JFR配套的工具JMC。

JMC

JDK Mission Control 是一个用于对 Java 应用程序进行管理、监视、概要分析和故障排除的工具套件。

在JDK14中,JMC是独立于JDK单独发行的。我们可以下载之后进行安装。

我们先启动一个程序,用于做JFR的测试。

@Slf4j
public class ThreadTest { public static void main(String[] args) {
ExecutorService executorService= Executors.newFixedThreadPool(10);
Runnable runnable= ()->{
while(true){
log.info(Thread.currentThread().getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
log.error(e.getMessage(),e);
}
}
}; for(int i=0; i<10; i++){
executorService.submit(runnable);
}
}
}

很简单的一个程序,启动了10个线程,我们启动这个程序。

然后再去看看JMC的界面:

我们可以看到在界面的左边已经可以看到运行在本机的ThreadTest程序了。

点击MBean服务器,可以看到该java程序的面板信息,里面包含CPU,堆栈信息。

在下面有7个tab分别是概览,MBean浏览器,触发器,系统,内存,线程,和诊断命令。

通过下面的tab我们可以获得更加详细的java程序的信息,并且通过触发器和诊断命令,我们还可以对目标java程序的JVM发送命令。

JMC非常强大,也有很多功能,具体的细节大家可以自己运行去体会。

因为本文主要是将JFR,下面我们将讲解如何在JMC中创建JFR和分析JFR。

创建JFR

上面右侧的MBean服务器下就是飞行记录器了,也就是我们的目标。

点击飞行记录器:

我们就可以开始创建一个JFR了。

目标文件就是JFR的生成地址,名称可以自己随便起一个,记录时间表示需要记录多长时间范围之内的JFR。

点下一步:

这一步可以选择更加详细的JVM参数。

点下一步:

这里,我们可以选择需要监控的Profile事件选项。可以按照你的需要进行选择。

最后点完成创建JFR。

分析JFR

上面我们的JFR记录了1分钟的Profile,在1分钟之后,我们可以看到目标JFR文件生成了。

生成完JFR之后,JMC会自动打开生成的JFR文件,我们得到一个大纲视图。

里面包含java应用程序,JVM内部,环境和事件浏览器。

事件浏览器中列出了我们在1分钟之内监控的事件。

JMC浏览器不仅可以监控本机的应用程序,也可以监控远程的应用程序。由于JMC的连接是通过JMX协议,所以远程java程序需要开启JMX协议的支持。

JFR事件

JMC好用是好用,但是要一个一个的去监听JFR文件会很繁琐。接下来我们来介绍一下怎么采用写代码的方式来监听JFR事件。

还是上面的图,如果我们想通过程序来获取“Class Loading Statistics"的信息,可以这样做。

上图的右侧是具体的信息,我们可以看到主要包含三个字段:开始时间,Loaded Class Count和 Unloaded Class Count。

我们的思路就是使用jdk.jfr.consumer.RecordingFile去读取生成的JFR文件,然后对文件中的数据进行解析。

相应代码如下:

@Slf4j
public class JFREvent { private static Predicate<RecordedEvent> testMaker(String s) {
return e -> e.getEventType().getName().startsWith(s);
} private static final Map<Predicate<RecordedEvent>,
Function<RecordedEvent, Map<String, String>>> mappers =
Map.of(testMaker("jdk.ClassLoadingStatistics"),
ev -> Map.of("start", ""+ ev.getStartTime(),
"Loaded Class Count",""+ ev.getLong("loadedClassCount"),
"Unloaded Class Count", ""+ ev.getLong("unloadedClassCount")
)); @Test
public void readJFRFile() throws IOException {
RecordingFile recordingFile = new RecordingFile(Paths.get("/Users/flydean/flight_recording_1401comflydeaneventstreamThreadTest21710.jfr"));
while (recordingFile.hasMoreEvents()) {
var event = recordingFile.readEvent();
if (event != null) {
var details = convertEvent(event);
if (details == null) {
// details为空
} else {
// 打印目标
log.info("{}",details);
}
}
}
} public Map<String, String> convertEvent(final RecordedEvent e) {
for (var ent : mappers.entrySet()) {
if (ent.getKey().test(e)) {
return ent.getValue().apply(e);
}
}
return null;
}
}

注意,在convertEvent方法中,我们将从文件中读取的Event转换成了map对象。

在构建map时,我们先判断Event的名字是不是我们所需要的jdk.ClassLoadingStatistics,然后将Event中其他的字段进行转换。最后输出。

运行结果:

{start=2020-04-29T02:18:41.770618136Z, Loaded Class Count=2861, Unloaded Class Count=0}
...

可以看到输出结果和界面上面是一样的。

JFR事件流

讲了这么多,终于到我们今天要讲的内容了:JFR事件流。

上面的JFR事件中,我们需要去读取JFR文件,进行分析。但是文件是死的,人是活的,每次分析都需要先生成JFR文件简直是太复杂了。是个程序员都不能容忍。

在JFR事件流中,我们可以监听Event的变化,从而在程序中进行相应的处理。这样不需要生成JFR文件也可以监听事件变化。

    public static void main(String[] args) throws IOException, ParseException {
//default or profile 两个默认的profiling configuration files
Configuration config = Configuration.getConfiguration("default");
try (var es = new RecordingStream(config)) {
es.onEvent("jdk.GarbageCollection", System.out::println);
es.onEvent("jdk.CPULoad", System.out::println);
es.onEvent("jdk.JVMInformation", System.out::println);
es.setMaxAge(Duration.ofSeconds(10));
es.start();
}
}

看看上面的例子。我们通过Configuration.getConfiguration("default")获取到了默认的default配置。

然后通过构建了default的RecordingStream。通过onEvent

方法,我们对相应的Event进行处理。

总结

本文讲解了JFR,JMC和JDK14的最新特性JFR event stream。希望能够对大家在工作中有所帮助。

本文的例子https://github.com/ddean2009/learn-java-base-9-to-20

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/jdk14-jfr-jmc-event-stream/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

JDK14的新特性:JFR,JMC和JFR事件流的更多相关文章

  1. JDK14的新特性-Switch新功能

    2020年3月17日,Oracle正式发布了JDK14版本,共新增了16项新特性 本文重点写一下关于switch的新功能: switch 表达式扩展了 switch 语句,使其不仅可以作为语句(sta ...

  2. JDK14的新特性:Lombok的终结者record

    目录 简介 新的Record类型 探讨Record的秘密 record扩展 总结 JDK 14的新特性:Lombok的终结者record 简介 自从面向对象产生之后,程序界就开始了新的变化,先是C发展 ...

  3. 乐字节-Java8新特性-接口默认方法之Stream流(下)

    接上一篇:<Java8新特性之stream>,下面继续接着讲Stream 5.流的中间操作 常见的流的中间操作,归为以下三大类:筛选和切片流操作.元素映射操作.元素排序操作: 操作 描述 ...

  4. JDK14的新特性

    文章目录 虽然JDK13在今年的9月17号才发布,但是丝毫不会影响到下一个版本JDK14的开发工作.听说官方定的新功能马上就要官宣了,我们这里不妨来提前推断一下. 在9月17号的发布中,Oracle提 ...

  5. 010-jdk1.8版本新特性二-Optional类,Stream流

    1.5.Optional类 1.定义 Optional 类是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. Optional 是个 ...

  6. JDK-7新特性,更优雅的关闭流-java try-with-resource语句使用

    前言 公司最近代码质量整改,需要对大方法进行调整,降低到50行以下,对方法的深度进行降低,然后有些文件涉及到流操作,很多try/catch/finally语句,导致行数超出规范值,使用这个语法可以很好 ...

  7. 阶段1 语言基础+高级_1-3-Java语言高级_08-JDK8新特性_第2节 Stream流式思想概述_3_流式思想概述

  8. Java 8新特性(一):Lambda表达式

    2014年3月发布的Java 8,有可能是Java版本更新中变化最大的一次.新的Java 8为开发者带来了许多重量级的新特性,包括Lambda表达式,流式数据处理,新的Optional类,新的日期和时 ...

  9. JDK14发布了,快来看看有什么新特性吧

    序言

  10. Jdk14 都要出了,Jdk9 的新特性还不了解一下?

    Java 9 中最大的亮点是 Java 平台模块化的引入,以及模块化 JDK.但是 Java 9 还有很多其他新功能,这篇文字会将重点介绍开发人员特别感兴趣的几种功能. 这篇文章也是 Java 新特性 ...

随机推荐

  1. Anaconda下载安装步骤

    下载地址 下载比较慢的,用迅雷下,点击复制地址,然后在迅雷里面直接创建连接 Anaconda基于python3.8 Anaconda基于python3.6的 安装步骤 没啥说的,一路安装,中间有个勾选 ...

  2. .net core6 Autofac依赖注入

    一.引言 .net core6在文件方面是精简了,所以配置方面也发生了部分变化:下面记录下.net core6中怎么配置Autofac 进行依赖注入. 二.项目创建 1).首先引用两个包:在Nuget ...

  3. java+文件实现的超市管理系统

    一.需求 1.使用java语言实现系统 2.数据存储使用io读写文件 3.超市的商品管理+销售功能 二.效果 商品列表 商品增删改查 购买 三.说明 1.开发工具/技术 java  eclipse 2 ...

  4. 【LeetCode栈与队列#02】有效括号

    有效括号 力扣题目链接(opens new window) 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右 ...

  5. 如何在矩池云使用 Poetry 管理项目环境

    官网介绍:Poetry is a tool for dependency management and packaging in Python. It allows you to declare th ...

  6. 【Azure Cache for Redis】Redis的导出页面无法配置Storage SAS时通过az cli来完成

    问题描述 在Azure Redis的导出页面,突然不能配置Storage Account的SAS作为授权方式. 那么是否可以通过AZ CLI或者是Powershell来实现SAS的配置呢? 问题解答 ...

  7. Java 数组嵌套

    1 public static void main(String[] args) 2 { 3 int[] arr = new int[] {8,6,3,1,9,5,4,7}; 4 int[] inde ...

  8. C语言之牛必克拉斯 main() 函数

    C语言之main()函数 C程序最大的特点就是所有的程序都是用函数来装配的.main()称之为主函数,是所有程序运行的入口.其余函数分为有参或无参两种,均由main()函数或其它一般函数调用,若调用的 ...

  9. Codeforces Round 170 (Div. 1)A. Learning Languages并查集

    如果两个人会的语言中有共同语言那么他们之间就可以交流,并且如果a和b可以交流,b和c可以交流,那么a和c也可以交流,具有传递性,就容易联想到并查集,我们将人和语言看成元素,一个人会几种语言的话,就将这 ...

  10. Openssl命令详解 - 密钥篇

    非对称算法密钥生成和公钥提取 # ecc算法 openssl ecparam -out pri.key -name prime256v1 -genkey #生成ec密钥,国密使用-name SM2 o ...