一 JFR
JFR的全称为Java Flight Recorder。其中Flight Recorder是飞行记录仪,即平时经常听到的“黑匣子”,用于记录飞行器所有的工作情况数据。
同样JFR也是用来记录Java应用程序在运行时的数据信息和各种发生的事件。
JFR最初名称为JRockit Flight Recorder,是高性能VM JRockit提供的一个高级商业特性,BEA公司收购Appeal,后Oracle又收购BEA公司后获得该项技术。
Java7 u40版本之后,在Oracle JDK中可以使用JFR功能,只是需要使用-XX:+UnlockCommercialFeatures启用此特性。
目前JFR成为JDK的一个内置功能,无论是Oracle JDK还是OpenJDK都附带,开发者可以自由使用。
我们都使用过jmap这样的工具,可以看到java进程中内存占用情况,也会使用jconsole/jvisualvm这类型图形工具,来观察java进程当前的信息。
和上述工具着眼点在某一个时点不同,JFR是记录一个时间段java进程的信息。

在这个时段内,java进程的关键信息,如内存占用,CPU利用率,网络端口情况,线程状态等都会被详细的记录下来,保存到一个压缩后的数据文件中,供后续分析。
通过对时间段中各个时点的状态信息和事件进行数据分析和图标式观察,我们就可以了解java应用具体的情况,哪里占用过多的资源,哪里需要排错,哪里可以调优。
事件的种类有
时点事件。比如抛出异常,开始线程等,表示在一个时间点发生的事件。
时段事件。表示一段时间内发生的事件,可以配置阈值。如垃圾回首,线程睡眠等。
可请求的事件。如方法探测样本,可以配置周期,设定事件频率。
JFR用三种方法来激活启动:
1. 通过启动Java程序时加入参数,如
java -XX:+FlightRecorder -XX:StartFlightRecording=duration=60s, filename=my.jfr MyApp
表示启动MyApp应用时,开始记录JFR信息60秒,数据保存在my.jfr中
2. 当java进程已经启动,通过jps等获得java进程号,就可以通过命令jcmd
jcmd 6636 JFR.start duration=60s filename=my.jfr
启动JFR记录60秒的数据,并保存到my.jfr文件中
3. 通过界面工具JMC来启动JFR,选定java进程,以及记录信息模板,输入时长。JFR开始记录信息并最后写入文件中。

如图,所有的JVM信息都可以用事件表述,写满线程缓存后放入链表串接的全局缓存中,再次写满后拷贝到磁盘文件中。
记录方式有
1. 固定时长。通过设置定长的时间来记录信息
-XX:StartFlightRecording=delay=20s, duration=60s
表示等待20秒后,持续记录60秒
2. 持续模式。可以在环型缓存中持续记录信息,这种方式一般由某个事件触发,比如CPU负载超过某个阈值,JVM退出前等等
启动参数
在 JVM 启动参数中加入-XX:+FlightRecorderOption=参数启动记录事件。参数为一个以逗号间隔的键值对数据。选项有以下:
name=name 指定记录的名称
dumponexit=true/false JVM推出时是否记录事件,默认为 false
settings=path JFR 配置文件的路径
delay=time 开始记录前的延时
duration=time 记录持续时间
filename=path 保存记录事件的文件路径
compress=true/false 是否使用 gzip 压缩记录数据,默认为 false
maxage=time 环形缓存中保存记录的数据的最长时间
maxsize=size 环形缓存占用的最大空间
JFR记录的事件通过模板来定义,在JDK的lib/jfr目录中,有两个文件default.jfc和profile.jfc,分别是默认模板和用于分析的模板。当然我们可以构建自己的模板用于不同的应用。
模板中定义了各种事件的触发条件,对于有阈值的事件,只有当达到某个设定的阈值后,事件才会被触发记录。过多的事件触发无疑会影响系统运行性能。
二 Java Mission Control
记录下JFR信息并保存到jfr文件中,可以通过JMC图形工具进行分析。
JMC是一个基于Eclipse SWT的图形工具,因为在预览版本中看到有中文包,所以可以用
/jmc-7.0.0-ea+03_linux-x64_bin/bin$ ./jmc -nl zh_CN
来启动使用中文语言
打开JFR记录文件,报告分为几大类,我们选择一些主要的进行说明:
1. Java应用程序。应用的总览,可以选择哪些指标一起显示。

线程:看到这个时段所有的线程概况,有直观的颜色表示,Block用红色,Park用灰色,Wait为黄色,正常为绿色。点击线程和对应时间块,就可以显示该时刻的线程状态。
内存:所有的类按照总分配量进行排序,点击某个类,可以显示对应的堆栈跟踪信息。同时也有内存堆的图示直观表示。

锁定实例:看到JFR这段时间有哪些类阻塞了多长时间,便于进行性能分析。

文件IO:文件IO总时间,读取写入字节数信息。
套接字IO:远程地址和端口的IO时间,读取写入字节数信息。
异常信息:有哪些异常被抛出,对应的消息,统计信息等。

2. JVM内部,JVM的相关信息。
垃圾收集:GC发生的次数,每次GC的名称,发生原因,暂停时长等。同时提供了内存堆和元空间的图表。

编译:JVM中动态编译持续的时长,对应的Java方法,编译代码大小等信息图表

类加载:时间段内加载和卸载的类统计图标
VM操作:VM操作的持续时间
TLAB分配:TLAB(Thread Local Allocation Buffer)线程本地分配缓存区,对应每个线程分配的内存,数目和总量统计图表。
3. 环境。提供java进程运行环境信息。
进程:这段时间内CPU占用率,进程列表等信息
4. 事件浏览器
JFR记录期间所有的事件,按照事件类型进行分类

JMC提供MBean系统的显示,和jvisualm工具类似,可以查看当前java进程mbean属性值。


JMC提供一个强有力的功能,触发器trigger,可以设定在某个条件下触发一个动作。
比如,当CPU负载超过5%以上持续2秒,就触发一个事件,往控制台上打印信息,发送邮件,当然也可以启动JFR进行信息收集。
========== 通知预警! ==========
A notification event has been triggered!
Notification creation time was: Tue Aug 28 16:36:15 CST 2018
The notification source is: [9] wildfly-14.0.0.Beta2/jboss-modules.jar (17795)
The notification rule is: Machine CPU Usage > 5 %
Type description:
attribute://java.lang:type=OperatingSystem/SystemCpuLoad
Rule trigger condition: value > 5 % for 2 seconds.
The condition was met for 2 seconds.
触发器可以执行的动作有
JMC应用告警,弹出对话框显示
控制台打印
启动JFR信息记录
HPROF信息导出
发送邮件
记录到文件

这个功能对于排错调优非常有用。除了通过JMC设置,也可以通过mbean编程设置触发器。(https://blogs.oracle.com/jtc/mimicking-java-flight-recorder-triggers-outside-java-mission-control)
在诊断命令界面上,可以很方便的调用JVM内部诊断命令进行数据查询和操作,功能和jcmd类似。

三 总结
JMC今年已经开源,源码仓库为http://hg.openjdk.java.net/jmc,这是个好消息。
虽然JMC从Java10后从OpenJDK中移除,但有独立包可以下载使用,后续随着Java11发布,JMC7的正式版本也会发布,目前预览版的下载地址为 http://jdk.java.net/jmc/
JFR目前一直是OpenJDK提供的核心能力,对应模块为jdk.jfr.jmod
一个不好的消息是,JMC开源之后,其开发组成员的Twitter信息显示,整个JMC开发团队被Oracle裁掉了。当然这些业界专家级人物都有好的出路,但对于JMC的发展还是会带来很多不确定的因素。
希望未来JMC/JFR还是能良好的发展,继续为OpenJDK提供一套强劲的免费开源分析工具。




