序
本文主要研究一下Java Flight Recorder的使用。
命令
主要有5个命令,configure、check、start、dump、stop。执行顺序的话,先start再dump,最后stop。
JFR.configure
参数 | 描述 | 值类型 | 默认值 |
---|---|---|---|
globalbuffercount | 指定global buffers的数量. 修改 memorysize 参数会影响该值. | Long | 默认值依赖 memorysize 参数. |
globalbuffersize | 指定global buffers大小, 单位bytes. 修改 memorysize 参数会影响到global buffers. | Long | 默认值依赖 memorysize 参数. |
maxchunksize | 指定单个data chunk的最大值, 单位bytes | Long | 12582912 |
memorysize | 指定总内存大小, 单位bytes | Long | 10485760 |
repositorypath | 指定recordings在写入到持久化文件之前的存储路径 | String | 默认为系统临时目录,Oracle Solaris以及Linux是/tmp .windows系统的话,取TMP 环境变量值 |
stackdepth | 指定stack traces的Stack depth | Long | 64 |
thread_buffer_size | 指定每个thread的Local buffer size, 单位bytes. 不建议修改此参数,可能会降低性能 | Long | 8192 |
threadbufferstodisk | 是否允许thread buffers在buffer thread阻塞的时候直接写到磁盘 | Boolean | false |
samplethreads | 是否开启thread sampling | Boolean | true |
命令实例
jcmd 5793 JFR.configure5793:Current configuration:Repository path: /private/var/folders/9r/v55wkcr91m5_g8h7lhgjzgr00000gn/T/2018_09_27_16_30_53_5793Stack depth: 64Global buffer count: 20Global buffer size: 512.0 kBThread buffer size: 8.0 kBMemory size: 10.0 MBMax chunk size: 12.0 MBSample threads: true复制代码
JFR.start
参数 | 描述 | 值类型 | 默认值 |
---|---|---|---|
delay | 指定延时多长时间才开始记录 | Integer类型加s表示秒, m 表示分钟, 或者h 表示小时 | 0s |
disk | 记录的时候是否写数据到磁盘 | Boolean | true |
dumponexit | 是否在JVM关闭时写记录到磁盘. 如果为true但没有指定filename , 则文件名为系统生成,包含process ID, recording ID,以及 current time stamp (例如,hotspot-pid-47496-id-1-2018_01_25_19_10_41.jfr ),文件路径为进程启动路径 . | Boolean | false |
duration | 指定记录时长 | Integer类型加s表示秒, m 表示分钟, 或者h 表示小时 | 0s (forever) |
filename | 指定停止时记录数据的文件路径,如果未指定,则使用进程使用目录,例如recording.jfr``/home/user/recordings/recording.jfr``c:\recordings\recording.jfr | String | No default value |
maxage | 指定记录数据在磁盘的最大存活时间,当disk参数为true时才有效 | Integer类型加s表示秒, m 表示分钟, 或者h 表示小时 | 0s (forever) |
maxsize | 指定记录数据在磁盘的最大大小,默认单位bytes,指定m 或M 表示兆,g 或G 表示G,只有当disk参数为true时才有效,该值不能比maxchunksize 参数值小. | Long | 0 (no maximum size) |
name | 指定记录文件名,如未指定则默认生成. | String | 默认为系统生成. |
path-to-gc-roots | JDK 10引入的,指定在记录结束前要收集的GC Roots的路径.该参数有助于排查内存泄露,但是收集比较耗时,当且仅当怀疑有内存泄露时才启用。如果settings 参数设置为profile , 则收集的信息包括潜在内存泄露对象的stack trace. | Boolean | false |
settings | 指定记录的配置文件,如果不是JRE_HOME/lib/jfr 目录下的要指定全路径,要指定多个的话,用逗号分隔。默认路径有default.jfc : 该配置开销低,可以用于持续运行.profile.jfc : 则提供比default更多的数据,但是开销大一些,对性能有所影响,适合短时间收集信息用 | String | JRE_HOME/lib/jfr/default.jfc |
命令实例
jcmd 5793 JFR.start name=demojfr dumponexit=true5793:Started recording 1. No limit specified, using maxsize=250MB as default.Use jcmd 5793 JFR.dump name=demojfr filename=FILEPATH to copy recording data to file.复制代码
JFR.check
参数 | 描述 | 值类型 | 默认值 |
---|---|---|---|
name | 指定文件名 | String | No default value |
verbose | 是否打印event settings | Boolean | false |
命令实例
jcmd 5793 JFR.check5793:Recording 1: name=demojfr maxsize=250.0MB (running)复制代码
JFR.dump
参数 | 描述 | 值类型 | 默认值 |
---|---|---|---|
filename (required) | 指定dump写入的路径,如果未指定,则使用进程启动的目录,例如:recording.jfr``/home/user/recordings/recording.jfr``c:\recordings\recording.jfr | String | No default value |
name (required) | 指定要dump的记录 | String | No default value |
path-to-gc-roots | JDK 10引入的,指定在记录结束前要收集的GC Roots的路径.该参数有助于排查内存泄露,但是收集比较耗时,当且仅当怀疑有内存泄露时才启用. | Boolean | false |
命令实例
jcmd 5793 JFR.dump name=demojfr filename=/tmp/demo.jfr5793:Dumped recording "demojfr", 480.8 kB written to:/tmp/demo.jfr复制代码
JFR.stop
参数 | 描述 | 值类型 | 默认值 |
---|---|---|---|
filename | 指定停止时数据写入的路径.如果没有指定则默认为进程启动的目录,例如recording.jfr``/home/user/recordings/recording.jfr``c:\recordings\recording.jfr | String | No default value |
name | 指定要stop的记录的名称 | String | No default value |
命令实例
jcmd 5793 JFR.stop name=demojfr5793:Stopped recording "demojfr".复制代码
JMC
JMC打开jfr文件实例截图如下:
读取JFR文件
@Test public void testReadJfr() throws IOException { Path p = Paths.get(getClass().getClassLoader().getResource("demo.jfr").getPath()); Listevents = RecordingFile.readAllEvents(p); events.stream() .forEach(e -> LOGGER.info("eventType:{},startTime:{},endTime:{},fields:{}",e.getEventType().getName(),e.getStartTime(),e.getEndTime(),e.getFields())); List eventNames = events.stream() .map(e -> e.getEventType().getName()) .distinct() .collect(Collectors.toList()); System.out.println(eventNames.toString()); }复制代码
- 直接使用jdk的api即可以解析jfr文件,读出RecordedEvent
- eventType类型输出如下:
[jdk.ExceptionStatistics, jdk.NativeMethodSample, jdk.ThreadSleep, jdk.JavaMonitorWait, jdk.CPULoad, jdk.JavaThreadStatistics, jdk.ClassLoadingStatistics, jdk.CompilerStatistics, jdk.ClassLoaderStatistics, jdk.ModuleExport, jdk.CodeCacheStatistics, jdk.CodeSweeperStatistics, jdk.GCConfiguration, jdk.ActiveSetting, jdk.ActiveRecording, jdk.InitialSystemProperty, jdk.InitialEnvironmentVariable, jdk.CPUInformation, jdk.CPUTimeStampCounter, jdk.ThreadAllocationStatistics, jdk.PhysicalMemory, jdk.NativeLibrary, jdk.CompilerConfiguration, jdk.CodeCacheConfiguration, jdk.CodeSweeperConfiguration, jdk.IntFlag, jdk.UnsignedIntFlag, jdk.LongFlag, jdk.UnsignedLongFlag, jdk.DoubleFlag, jdk.BooleanFlag, jdk.StringFlag, jdk.ThreadEnd, jdk.ThreadCPULoad, jdk.NetworkUtilization, jdk.ThreadStart, jdk.ThreadContextSwitchRate, jdk.GCSurvivorConfiguration, jdk.GCTLABConfiguration, jdk.GCHeapConfiguration, jdk.YoungGenerationConfiguration, jdk.SystemProcess, jdk.ThreadDump, jdk.JVMInformation, jdk.OSInformation, jdk.ModuleRequire]复制代码
除了系统定义的eventType,还可以自定义event
自定义event
- DemoEvent
@Label("Demo Event")@Description("Helps the programmer getting started")public class DemoEvent extends Event { @Label("Message") private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; }}复制代码
- 发布事件
DemoEvent event = new DemoEvent(); event.setMessage("hello, world!"); event.commit();复制代码
之后使用api解析jfr文件,可以看到自定义的event,其name为com.example.jfr.DemoEvent 可以使用如下参数启动-XX:StartFlightRecording=duration=120s,filename=/tmp/event.jfr,settings=default,name=DemoEventRecording
相关模块
JDK11关于jfr的模块有两个,分别是jdk.jfr.jmod以及jdk.management.jfr.jmod,其具体内容如下:
➜ jmods ../bin/jmod describe jdk.jfr.jmodjdk.jfr@11exports jdk.jfrexports jdk.jfr.consumerrequires java.base mandatedqualified exports jdk.jfr.internal.management to jdk.management.jfrcontains jdk.jfr.eventscontains jdk.jfr.internalcontains jdk.jfr.internal.cmdcontains jdk.jfr.internal.consumercontains jdk.jfr.internal.dcmdcontains jdk.jfr.internal.handlerscontains jdk.jfr.internal.instrumentcontains jdk.jfr.internal.jfccontains jdk.jfr.internal.settingscontains jdk.jfr.internal.testcontains jdk.jfr.internal.typesplatform macos-amd64➜ jmods ../bin/jmod describe jdk.management.jfr.jmodjdk.management.jfr@11exports jdk.management.jfrrequires java.base mandatedrequires java.management transitiverequires jdk.jfrrequires jdk.managementprovides sun.management.spi.PlatformMBeanProvider with jdk.management.jfr.internal.FlightRecorderMXBeanProvidercontains jdk.management.jfr.internalplatform macos-amd64复制代码
小结
- Java Flight Recorder是一款优秀的java应用诊断工具,以前是商业版的特性,现在在java11当中开源出来,它导出的jfr文件可以用Java Mission Control来分析。
- JDK11内置了相关API,可以用来解析jfr文件,也可以在应用程序自定义事件发布出来
- JFR可以采用JVM命令启动,也可以使用jcmd的JFR.开头的命令在运行时操作,非常方便