全系列目录:通过 JFR 与日志深入探索 JVM - 总览篇
我们都知道,黑匣子是用于记录飞机飞行和性能参数的仪器。在飞机出问题后,用于定位问题原因。JFR(Java Flight Record) 就是 Java 的黑匣子。
JFR 是 Java Flight Record (Java飞行记录) 的缩写,是 JVM 内置的基于事件的JDK监控记录框架。这个起名就是参考了黑匣子对于飞机的作用,将 Java 进程比喻成飞机飞行。顾名思义,这个记录主要用于问题定位和持续监控。
在线上出问题时,我们一般首要任务是快速恢复,而不是保留现场等问题定位好解决好。而快速恢复一般需要重启,或者简单改一些代码之后发布重启,或者回滚到上一个版本的代码之后发布重启,这些都会破坏现场。持续采集的意义就在于事后定位分析问题,JFR 就是这样一种主要用于问题定位和持续监控的 Java 事件记录。
如果是利用默认配置启动这个记录,性能非常高效,对于业务影响很小(当然,对于大部分应用是这样的,对于某些特殊的应用,例如线程密集有好几万线程的应用,或者是内存特别大达到几个 TB 级别的进程,默认的 JFR 配置可是不行的),因为这个框架本来就是用来长期在线上部署的框架。这个记录可以输出成二进制文件,用户可以指定最大记录时间,或者最大记录大小,供用户在需要的时候输出成文件进行事后分析。
JFR 的前身也是 JFR,只不过这个 J 不是 Java 而是 JRockit。在 JRockit 虚拟机时代,就有这样一个工具用来记录 Java 虚拟机运行时各项数据。在 Oracle 收购 Sun 公司之后,Hotspot 虚拟机时代,也一直延续了这个工具:
JFR 起源于 JVM 基于事件的监控(JEP 167: Event-Based JVM Tracing),这个 JEP 中定义了一些基本的监控事件,JFR 在此基础上做了很多扩展与补充。同时这个 JEP 中只是简单地将这些监控事件输出到标准输出(stdout),JFR 则是更加完善。
这里我们先来列出一些些关于JFR更新与bug信息的链接:
因为某些异常很难在开发测试阶段发现,需要在生产环境才会出这些问题。为了能在生产问题发生后,更好的定位生产问题,JDK 提供了这样一个可以长期开启,对应用影响很小的持续监控手段。官方说,目标是开启 JFR 监控(默认配置),对性能的影响在1%之内,对JVM Runtime 和 GC,OS 以及 Java 库进行全方位的监控。
这里放出一个本人开启默认配置的 JFR 监控后,性能对比,JFR 是在 19:40 开启的:
可以看出,在 19:40 开启 default,之后请求数量回归峰值之前,CPU Load 基本和之前一样,可以视为无影响。
再放出一个本人在同一个微服务另一个实例同一时间开启 profile 配置的 JFR 监控后,性能对比,同样是在 19:40 开启:
profile 的情况下,峰值 CPU Load 相较于 default 的峰值 CPU Load 高了很多。profile 配置官方说大概影响 2% 的性能,但是实际上,这个影响,尤其是频繁发生内存分配的微服务接口应用,影响绝对不止 2%,而且profile的确采集的东西要比默认配置的多很多(这个我们后面会详细说,为什么负载会高的原因也会在后面的系列详细分析说),所以,线上系统不推荐长期跑profile。
在配置正确的情况下,JFR 的整体开销是很低的(为啥低会在下一小节详细说明)。JFR 基于事件采集,JFR 提供很多类型的监控事件,用户可以自由的启用或者关闭某些事件,并定义事件采集的条件,周期等等,还可以采集这些事件发生的时候的线程堆栈,GC 根节点等等。这些事件包括如下几类(可能类型之间会有交叉):
可以根据自己的需要,针对 JVM 的这些事件进行持续的监控。
如上面所述,JFR 对于 JVM 所处运行环境也是有采集统计数据的事件的。
对于一些性能关键事件,例如 Java 锁相关事件,IO 相关事件,GC 相关事件等等,这些和程序性能息息相关的事件,经过 JFR 的持续采集,为性能瓶颈定位提供了有力的参考。
想不浮于表面看 JVM 的各种概念以及设计原理,而是深入了解 JVM 的原理以及了解这些原理后如何运用于实践, JFR 会是一个不错的学习工具。你可以通过学习 JVM 原理,结合 JFR 事件与一些 JVM 日志,通过测试程序产生这些事件,了解这些事件产生的原因,时机以及影响,来更深入的理解 JVM 原理。这也是本系列的一个主要目标
JFR 有很多统计数据,这些是你的 JVM 程序可以解析的。在 Java 14 更是引入了 JFR Stream,这样就不用像之前那样想解析必须先 dump 到一个临时文件中进行解析,而是像流一样不断消费。利用这些统计数据,你可以动态的采集一些 JFR 事件。这些事件可能对于性能影响较大,不能线上一直持续采集,但是又是那种遇到线上问题非常需要的事件,这样动态开启的需求就很必要。通过对于 JFR 一些性能影响小的持续采集事件的消费,可以在出现问题时,动态开启一些 JFR 事件的采集,这样能更好的维持线上稳定以及高效,并能保证事后定位解决问题时有据可依。