JDK Flight Recorder (JFR) User Guide
Co-authors: Gireesh Punathil, Ravali Yatham
Introduction
JDK Flight Recorder (JFR) is an integrated monitoring and profiling capability introduced in IBM Semeru Runtimes in v11.0.27, v17.0.15 and v21.0.7 releases. JFR aims to provide industry standard, low overhead, continuous workload monitoring and diagnostics experience to the users of Semeru based Java workload running in containerized or traditional deployment targets.
JFR is an integral component of OpenJ9, the JVM that powers IBM Semeru Runtimes. The main function of JFR is to run in conjunction with the running Java application, collect application level and JVM level metrics, record these metrics in the form of a JFR record. JFR records are binary files, and hence another tool is required to extract, interpret and visualize the JFR record. JDK Mission Control (JMC) extracts and helps in visualizing the metrics data.
JFR use cases
Java applications (either in the development or in the production phase), that need continuous profiling of its various components to improve the performance characteristics are ideal use cases of JFR. For example, JFR can be used to inspect and track the CPU consumption patterns of the application methods, garbage collection pauses, class loading events, and lock contention and gain valuable insights about the application’s behavior.
JFR acts as a proactive tool for capturing real-time data, helping to optimize resource usage and improve overall system performance.
Setup and configuration
Make sure that the Java version in use supports JFR. JFR capability was introduced in Semeru Runtimes in the v11.0.27, v17.0.15 and v21.0.7 releases.
Starting from these versions, Flight Recorder is enabled by default, allowing the JVM to collect diagnostic data on demand. While no metrics are recorded initially, this setup ensures that data collection can be triggered when needed without requiring additional JVM startup options.
To control whether JFR is enabled or disabled in the JVM, use the -XX:[+|-]FlightRecorder option

Note on z/OS:
Explicitly enable the attach API with -Dcom.ibm.tools.attach.enable=yes
on the target JVM in order for jcmd to discover and connect to it.
Refer to IBM Semeru Runtime Certified Edition for z/OS for more information on attach API.
Collecting JFR recordings
JFR recordings can be captured using the jcmd tool, which allows attaching to a running JVM and triggering collection commands as needed. This method provides the flexibility to capture recordings when an anomaly is suspected or when runtime diagnostics are necessary. The jcmd tool enables initiating profiling and diagnostic recordings manually, as recordings do not start automatically and must be explicitly triggered.
To start a JFR recording, do the following:
1. Identify the target JVM:
$ jcmd -l
From this list, identify the target application and note down the JVM ID (the number in the first column) to be used later for all the jcmd commands.

2. Start the recording:
a) $ jcmd <vmid> JFR.start [filename=<file>] [duration=<time>]
<file> specifies the absolute name of the file where the recording will need to be saved.
The file name should have a .jfr extension. The visualizer tools will not recognize any other extensions.
If a file name is not specified, the recording is saved as defaultJ9recording.jfr in the working directory of the process.
<time> specifies the duration of the recording with a unit token appended to the duration.
Unit tokens for the duration are s (seconds), m (minutes), h (hours), and d (days).
Specify only one unit of time, for example, 54s, 12m, 1h, or 2d.
If the duration is not specified, the tool continues to record until the recording is manually stopped or the VM is shut down.
An example jcmd command:
$ jcmd 121147 JFR.start filename=/home/hg/peak.jfr duration=30s
b) The JFR.start command also supports a comma-separated format for options:
$ jcmd 121147 JFR.start filename=/home/hg/peak.jfr,duration=30s
This starts a recording on the process with ID 121147, collecting for 30 seconds and saving it to /home/hg/peak.jfr file.
c) Alternative method is to use the Main class name instead of process ID
$ jcmd Foo JFR.start filename=/home/hg/peak.jfr duration=60s
or comma-separated format
$ jcmd Foo JFR.start filename=/home/hg/peak.jfr,duration=60s
This approach eliminates the need to manually fetch the PID. The recording is started on the application whose main class is Foo, saving the data for 60 seconds to the specified file.
3. Dump the recording:
$ jcmd <vmid> JFR.dump [options]
filename (Optional) :Name of the file to which the flight recording data is written.
This command dumps the ongoing recording to a file without stopping the recording.
4. Stop the recording:
To stop the recording manually before the duration ends, or when no duration is set, use the JFR.stop command.
$ jcmd <vmid> JFR.stop
This command stops the ongoing recording and commits the recordings into the output file.
Validating, inspecting and analyzing JFR recordings
Some common commands that can be used on the JFR recordings are as follows.
View summary of the recording
To obtain a high-level summary of the JFR recording, use the following command:
$ jfr summary peak.jfr

This will provide an overview of the JFR recording, including the number of events, size of each event ,event counts and other relevant details about the recording.
View metadata of the recording
To display metadata information from the JFR recording, use the following command:
$ jfr metadata peak.jfr

This command prints the JFR metadata such as event structures and the data types of the fields.
Print all events
To display all the events captured in a JFR recording, use the following command:
$ jfr print peak.jfr

This displays the full list of events contained in the peak.jfr file.
Print specific events
To print specific events captured during the recording, use the jfr print command with the --events option:
$ jfr print --events “jdk.ExecutionSample” peak.jfr

Print events in XML format
To output the event data in XML format, use the following command:
$ jfr print --xml --events "jdk.ExecutionSample" peak.jfr
Print events in JSON format
To output the event data in JSON format, use the following command:
$ jfr print --json --events "jdk.ExecutionSample" peak.jfr
Print events with stack depth
To limit the number of frames when printing events that contain method call stacks, use the following command:
$ jfr print --stack-depth 10 --events "jdk.ExecutionSample" peak.jfr
Note that the default frame count is 5.
Visualization
JFR data can be visualized and analyzed using the JDK Mission Control (JMC) tool. JMC provides detailed charts, graphs, and performance metrics, enabling the users to examine system information and event data collected during a JFR recording. It helps users to gain insights into application behavior, identify performance bottlenecks, and monitor overall system health, using high level and convenient visualizations.
For example, the environmental variables metric is listed as shown in the following screenshot.

Latest version of JMC can be downloaded using this link:
https://www.oracle.com/java/technologies/javase/products-jmc9-downloads.html
JFR troubleshooting tips
Some of the common issues encountered while using JFR, and the associated resolutions / workarounds are as follows.
-
Issue: JFR not starting
Solution: Ensure that -XX:+FlightRecorder is enabled.
-
Issue: JFR not starting, the option is silently ignored
Solution: Ensure that the Java in use supports JFR. See the support matrix in the Introduction section.
-
ChatGPT said:
Issue: jcmd command not found
Solution: Make sure $JAVA_HOME/bin is in the path.
-
Issue: Large dump sizes
Solution: Check the specified duration for record collection. Long durations like hours are bound to create large dumps.
-
Issue: Recording file not found or empty
Solution: Verify that the output path is correct and that the intended events are being captured during recording.
- Issue: Error in command, One JFR recording is in progress
Solution: Currently OpenJ9 allows only one recording at a time. Stop the recording to start a new one with different configurations.