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.
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
You don’t have to restart your JVM to enable JFR
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
In essence the three JFR commands
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
$ 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
$ jcmd <pid> JFR.start name=rec
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
For JDK 8 you need to specify the numerical identifier using
In JDK11 and above you specify the numerical id using
In order to get a running recording written to file, you use the
In JDK8 you must define either
name to the
JFR.dump command, as well as a defined
$ jcmd <pid> JFR.dump name=rec filename=recording.jfr
In JDK11 and above if you omit the
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 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).
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:
||Name that can be used to identify recording, e.g. "My Recording"|
||Resulting recording filename, e.g. "C:\Users\user\My Recording.jfr"|
||Delay recording start with (s)econds, (m)inutes), (h)ours), or (d)ays, e.g. 5h.|
||Duration of recording in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g. 300s.|
||Dump running recording when JVM shuts down|
||Recording should be persisted to disk|
||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|
||Maximum amount of bytes to keep (on disk) in (k)B, (M)B or (G)B, e.g. 500M, or 0 for no limit|
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.
For JDK 8 you need to unlock commercial features in order to use
> 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
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.
In JDK 11 and later you just add the
> java -XX:StartFlightRecording=filename=dump.jfr,dumponexit=true -jar MicronautServer-1.jar
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
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`
Most options to
StartFlightRecording can be recognized from the
|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|
There are a few options that can cause confusion; the options
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,
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
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
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.