jaokim's blog

JVM sustaining engineer at Oracle.

31 August 2022

Using the JDK Flight Recorder


Photo of a tape recorder
Photo by Markus Spiske.


The JDK Flight Recorder, JFR, is an integral part of the HotSpot JVM. I’m assuming you kind of know what JFR is, but in short it collects data about the JVM as well as the Java application running on it.

This blog post is a quick guide on how to use JFR. To try and be less confusing on the slight differences between mostly JDK8 and newer versions, you can choose your flavor using the buttons below to hide/show relevant parts. To further confuse everything, if you’re on OpenJDK8, JFR works more like it does on JDK11 (Open or not) and above, since it was backported from 11 to 8. If you’re however on Oracle JDK8, it slightly differs from OpenJDK8 and JDK11 and up.


There are basically two ways to start a JFR recording, either at startup by supplying command-line arguments, or during runtime by using the jcmd tool. You first however must make sure you have JFR enabled… or do you? Actually, since JDK 8u40 (i.e. since 2015) you don’t have to explicitly enable it on startup.

Using the JDK Flight Recorder

One relatively common misconception is that you have to restart the JVM if you haven’t enabled JFR on startup. This is however not true, you can easily enable JFR in a running process using jcmd.

You don’t have to restart your JVM to enable JFR

With jcmd, you can control various features in a running JVM. You can either use the Process ID (pid), or target any JVM running a particular main-class. In order to get the pid, you can run jcmd without arguments to get a list of all running Java applications.

$ jcmd
27016 MicronautServer-1.0.jar
29784 jdk.jcmd/sun.tools.jcmd.JCmd

Starting and Saving Recording with jcmd

In essence the three JFR commands start, dump, and stop are all you need.

$ jcmd <pid> JFR.start name=rec
$ jcmd <pid> JFR.dump name=rec
$ jcmd <pid> JFR.stop name=rec

In Oracle JDK8, JFR is a commercial feature, meaning that you either have to add -XX:+UnlockCommercialFeatures to the command line when starting the JVM, or you can unlock commercial features in runtime using jcmd and the VM.unlock_commercial_features command.

$ jcmd <pid> VM.unlock_commercial_features

With commercial options unlocked, you can go ahead and start your recording.

You start a recording with the command JFR.start.

$ jcmd <pid> JFR.start name=rec

The name argument is an optional identifier for the recording. If not defined the JVM will choose a numerical identifier, which you’ll need to supply when using JFR.dump or JFR.stop.

For JDK 8 you need to specify the numerical identifier using recording=x .

In JDK11 and above you specify the numerical id using name=x.

In order to get a running recording written to file, you use the JFR.dump command.

In JDK8 you must define either recording or name to the JFR.dump command, as well as a defined filename.

$ jcmd <pid> JFR.dump name=rec filename=recording.jfr

In JDK11 and above if you omit the name from JFR.dump all recordings will be dumped. If you omit the filename, an automated generated filename will be used

$ jcmd <pid> JFR.dump name=rec filename=recording.jfr

To stop a recording, you use the JFR.stop command. If you also add the filename argument to the command, the recording is also saved to disk.

$ jcmd <pid> JFR.stop name=rec


Conceptual image showing the timeline of a JVM, with JFR registering events first when 'JFR.start' is issued. Actual recordings are saved and persisted to disk using 'JFR.dump', or 'JFR.stop'; the latter also stopping the recording.
Conceptual image of a JVM process where a recording was started using JFR.start, and then written to file (rec1.jfr) with JFR.dump, and finally stopped with a final write to file (rec2.jfr).


Jcmd JFR options

The options for each of the JFR commands can be printed using the jcmd help command.

$ jcmd <pid> help JFR.start

Below are some of the more commonly used, and important options:

Option Description
name Name that can be used to identify recording, e.g. "My Recording"
filename Resulting recording filename, e.g. "C:\Users\user\My Recording.jfr"
delay Delay recording start with (s)econds, (m)inutes), (h)ours), or (d)ays, e.g. 5h.
duration Duration of recording in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 300s.
dumponexit Dump running recording when JVM shuts down
disk Recording should be persisted to disk
maxage Maximum time to keep recorded data (on disk) in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 60m, or 0 for no limit
maxsize Maximum amount of bytes to keep (on disk) in (k)B, (M)B or (G)B, e.g. 500M, or 0 for no limit

Starting a Flight Recording at JVM Startup

A common use-case is to have a so-called default recording upon starting the JVM. The perks of this is you don’t have to do anything manually, and you can have JFR dump a recording whenever the JVM decides to exit for whatever reason – giving you ideas of what that “whatever” was.

To start a recording when the JVM starts you use the -XX:StartFlightRecording argument. The options are basically the same as when starting using jcmd shown above.

JDK 8

For JDK 8 you need to unlock commercial features in order to use StartFlightRecording.

> java -XX:+UnlockCommercialFeatures -XX:StartFlightRecording=name=default,filename=dump.jfr,dumponexit=true,maxsize=250M -jar JettyServer-1.jar
Started recording 1.

Use JFR.dump name=default to copy recording data to file.

The default maxsize is currently unlimited in JDK 8 when using StartFlightRecording=name=default. This means you could fill up your temporary directory, unless you give an explicit size or age boundary using maxsize or maxage. In the example above I’ve chosen to set maxsize explicitly to 250 MB, which is the default for later JDK versions.

If you’re however used to starting your default recording with FlightRecordingOption=defaultrecording=true, the recording will get a default maxage of 15 minutes.

JDK 11 and Above

In JDK 11 and later you just add the StartFlightRecording argument.

> java -XX:StartFlightRecording=filename=dump.jfr,dumponexit=true -jar MicronautServer-1.jar

With the dumponexit argument set to true, you’ll get a recording when the JVM exits.

If you want to dump the recording at any other time, you can use the JFR.dump command together with a filename.

> jcmd <PID> JFR.dump name=default filename=ongoing-recording.jfr


The default recording registers events from the beginning of the JVM startup, and these events can be saved to file using 'JFR.dump' and the default recording's name. Another recording can be started in parallel using 'JFR.start' and corresponding 'JFR.dump', or 'JFR.stop'. At JVM exit, the default recording is saved to disk, containing only as much data as defined by 'maxage' or 'maxsize' In this image a default recording is started when the JVM starts with the `StartFlightRecording` option. One can see that the default recording is active throughout the JVM's lifetime (from 00:01 to 00:27).
Somewhere in the middle we start another recording named "`rec`" (the yellowish blocks) for a shorter period, which we first dump to `rec1.jfr`, and then stops with a write to `rec2.jfr`.
After some time we also write the default recording to `def.jfr` using JFR.dump, and at the JVM exit, the default recording is written to `dump.jfr`


StartFlightRecording Options

Most options to StartFlightRecording can be recognized from the JFR.start command.

Option Description
name Optional name to identify the recording
delay Start recording after a delay
duration Only record for a certain time
filename Filename where recording should be stored
disk Specifies whether to write data to disk while recording.
maxage List of settings files
maxsize List of settings files

Some Often Misunderstood Options

There are a few options that can cause confusion; the options disk, maxage and maxsize. Setting disk to true will make JFR store temporary data on disk instead of in-memory. Storing temporary data on disk will give you more data for the recording, as opposed to storing in-memory, since the default in-memory buffers are by default smaller. The two latter, maxage and maxsize are only used if disk=true. These settings control how much temporary data JFR should store, they should not be used to “filter” the data. The maxage defines how long events are stored in the temporary directory; if you use maxage=1h you’d get data that is atleast 1 hour old. You could also get data older than 1 hour, if for instance the event fits within the chunks of 12 MB that JFR defaults to, or if the event is a long running event. Setting maxsize limits how much data is stored in the temporary directory, and with the default chunksize, it only makes sense to set it above 12 MB.

If you want a recording for a limited time period, you should use the duration, and possibly delay option.

When setting delay=5m, and duration=15m, the recording will start after 5 minutes, and record for 15 minutes.
When setting `delay=5m`, and `duration=15m`, the recording will start after 5 minutes, and record for 15 minutes.

For instance, if you issue the below JFR.start jcmd at 08:00, the delay=4h will make your recording start at 12:00, and duration will give you 15 minutes worth of JFR data in the diagnosis.jfr file. The delay option can also be used just to delay recording only a few seconds in order to get the JVM to “warm up”, as to not record JVM internal processes such as JIT compilation, etc – depending on your needs.

jcmd 27016 JFR.start delay=4h,duration=15m,filename=diagnosis.jfr

Reference

A good starting page for JFR related documentation is the Oracle Java components landing page, where you continue to the JDK Mission Control (JMC) documentation. In general you should always be using the latest JMC (8 as of writing) for analyzing JFR files, regardless of which JDK version you’re using. So choose JMC 8 in the drop-down, and then consult the right hand links on the JMC documentation page, “Java/JDK Flight recorder” for your JDK.

tags: HotSpot - JFR