乐趣区

Java Flight Recorder小试牛刀


本文主要研究一下 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.configure
5793:
Current configuration:

Repository path: /private/var/folders/9r/v55wkcr91m5_g8h7lhgjzgr00000gn/T/2018_09_27_16_30_53_5793

Stack depth: 64
Global buffer count: 20
Global buffer size: 512.0 kB
Thread buffer size: 8.0 kB
Memory size: 10.0 MB
Max chunk size: 12.0 MB
Sample 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=true
5793:
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.check
5793:
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.jfr
5793:
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=demojfr
5793:
Stopped recording “demojfr”.
JMC
JMC 打开 jfr 文件实例截图如下:
读取 JFR 文件
@Test
public void testReadJfr() throws IOException {
Path p = Paths.get(getClass().getClassLoader().getResource(“demo.jfr”).getPath());
List<RecordedEvent> events = RecordingFile.readAllEvents(p);
events.stream()
.forEach(e -> LOGGER.info(“eventType:{},startTime:{},endTime:{},fields:{}”,e.getEventType().getName(),e.getStartTime(),e.getEndTime(),e.getFields()));
List<String> 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.jmod
jdk.jfr@11
exports jdk.jfr
exports jdk.jfr.consumer
requires java.base mandated
qualified exports jdk.jfr.internal.management to jdk.management.jfr
contains jdk.jfr.events
contains jdk.jfr.internal
contains jdk.jfr.internal.cmd
contains jdk.jfr.internal.consumer
contains jdk.jfr.internal.dcmd
contains jdk.jfr.internal.handlers
contains jdk.jfr.internal.instrument
contains jdk.jfr.internal.jfc
contains jdk.jfr.internal.settings
contains jdk.jfr.internal.test
contains jdk.jfr.internal.types
platform macos-amd64

➜ jmods ../bin/jmod describe jdk.management.jfr.jmod
jdk.management.jfr@11
exports jdk.management.jfr
requires java.base mandated
requires java.management transitive
requires jdk.jfr
requires jdk.management
provides sun.management.spi.PlatformMBeanProvider with jdk.management.jfr.internal.FlightRecorderMXBeanProvider
contains jdk.management.jfr.internal
platform macos-amd64
小结

Java Flight Recorder 是一款优秀的 java 应用诊断工具,以前是商业版的特性,现在在 java11 当中开源出来,它导出的 jfr 文件可以用 Java Mission Control 来分析。
JDK11 内置了相关 API,可以用来解析 jfr 文件,也可以在应用程序自定义事件发布出来
JFR 可以采用 JVM 命令启动,也可以使用 jcmd 的 JFR. 开头的命令在运行时操作,非常方便

doc

Using Java Flight Recorder
Java Flight Recorder Cheat Sheet
2 Diagnostic Command Reference
Clarifying some confusion around Java Flight Recordings
Java Flight Recorder:The Secret Arrow in Your Quiver

退出移动版