Java Flight Recorder and JDK Mission Control: recording production safely

JDK Flight Recorder (JFR) is an event recorder built into the HotSpot JVM. It captures GC, compilation, threads, locks, I/O, allocation and CPU samples into a compact binary file, with an overhead low enough for continuous use in production. Most of what an external profiler or agent would tell you, JFR can already record.
Availability
JFR was open-sourced and contributed to OpenJDK in JDK 11 by JEP 328, and it is part of standard OpenJDK builds since then. JEP 349 added event streaming in JDK 14, which lets code consume events from a running recording. The Oracle troubleshooting guide describes JFR as integrated into the JVM with very small performance overhead, so it can be used in production environments.
Two templates: default and profile
A JFR configuration is a .jfc file that enables events and sets thresholds and sampling periods. The JDK ships two in $JAVA_HOME/lib/jfr/:
default.jfc: the troubleshooting guide describes it as collecting a predefined set of data with low overhead. This is the one to leave running continuously.profile.jfc: more data, with more overhead. Use it for a few minutes while investigating, for example to get denser CPU samples or allocation data.
The jfr configure command of recent JDKs can create a custom .jfc from these templates; see the jfr command reference.
Starting a recording
From the command line
# continuous recording: keep the last 6 hours or 500 MB on disk, write a file on exit
-XX:StartFlightRecording=name=continuous,settings=default,maxage=6h,maxsize=500m,dumponexit=true,filename=/var/log/app/
The java reference lists the parameters of -XX:StartFlightRecording, including delay, disk, dumponexit, duration, filename, name, maxage, maxsize, path-to-gc-roots and settings, and notes that the option is equivalent to the JFR.start diagnostic command.
On a running JVM with jcmd
No restart is needed. The jcmd reference documents JFR.start, JFR.dump, JFR.check, JFR.stop and JFR.configure:
# a time-limited profiling recording
$ jcmd <pid> JFR.start name=incident settings=profile duration=5m filename=/var/tmp/incident.jfr
# what is recording right now?
$ jcmd <pid> JFR.check
# write the last 15 minutes of the continuous recording; it keeps running
$ jcmd <pid> JFR.dump name=continuous maxage=15m filename=/var/tmp/last15m.jfr
# stop a recording
$ jcmd <pid> JFR.stop name=incident
The JFR.dump pattern is the reason to keep a continuous recording: when an incident happens, you dump the minutes before it, instead of starting a recording after the fact and hoping the problem comes back. File names accept %p (process ID) and %t (timestamp).
Events that answer common questions
| Question | Events |
|---|---|
| Where does CPU go? | jdk.ExecutionSample, jdk.CPULoad, jdk.ThreadCPULoad |
| Are GC pauses the problem? | jdk.GarbageCollection, jdk.GCPhasePause, jdk.GCHeapSummary |
| Who allocates? | jdk.ObjectAllocationSample (JDK 16 and later), jdk.ObjectAllocationInNewTLAB, jdk.ObjectAllocationOutsideTLAB |
| What is leaking? | jdk.OldObjectSample, with path-to-gc-roots=true for reference chains |
| Where do threads wait? | jdk.JavaMonitorEnter, jdk.JavaMonitorWait, jdk.ThreadPark |
| Is I/O slow? | jdk.SocketRead, jdk.SocketWrite, jdk.FileRead, jdk.FileWrite |
| Exceptions? | jdk.JavaErrorThrow, jdk.ExceptionStatistics |
| Virtual threads pinned? | jdk.VirtualThreadPinned (see virtual threads) |
| JVM configuration at the time | jdk.JVMInformation, jdk.InitialSystemProperty, jdk.GCConfiguration |
Many events have a duration threshold: only waits or I/O operations longer than the threshold are recorded, which is how overhead stays low. The profile template uses lower thresholds than default.
path-to-gc-roots=true makes the recorder compute reference chains for old object samples when the recording ends or is dumped, which the jcmd reference warns can pause the application. Use it on a recording dedicated to a leak investigation.
Reading a recording
The jfr command
The JDK includes the jfr tool to inspect files without a GUI:
# which events, how many, how big
$ jfr summary incident.jfr
# print selected events as text or JSON
$ jfr print --events jdk.GCPhasePause incident.jfr
$ jfr print --json --events jdk.ExecutionSample incident.jfr > samples.json
On JDK 21 and later, jfr view prints predefined aggregated views, such as hot methods or GC pauses, directly in the terminal; run jfr --help view for the list available in your release.
JDK Mission Control
JDK Mission Control (JMC) is the graphical analyser for JFR files. It is distributed separately from the JDK, as an open-source project with builds from several vendors (see the OpenJDK JMC project page). Open a .jfr file and start with the Automated Analysis page, which flags common problems with a score and an explanation, then move to the pages for method profiling, memory, locks, I/O and GC. JMC can also connect to a live JVM over JMX and start recordings on it.
From code
The jdk.jfr.consumer API reads recordings and streams live events, and jdk.jfr.Event lets an application define its own events (for example, one per request with its route and duration) that then appear next to the JVM's own.
try (var rs = new RecordingStream()) {
rs.enable("jdk.GCPhasePause");
rs.onEvent("jdk.GCPhasePause", e ->
System.out.println(e.getDuration().toMillis() + " ms " + e.getString("name")));
rs.start();
}
Practical rules
- Run a continuous
defaultrecording withmaxageormaxsizeon every production JVM, writing to a volume that survives restarts. - Dump with
JFR.dumpas soon as an incident starts; keep the file with the incident record. - Switch to
profilefor a short, targeted recording when you need more detail. - Recordings can contain sensitive values, such as system properties, environment-derived configuration and strings in events. Store them like logs with personal data; the
jfr scrubsubcommand can remove selected events, categories or threads from a copy before you share it.
Sources
- JEP 328: Flight Recorder and JEP 349: JFR Event Streaming
- Java Platform Troubleshooting Guide: Diagnostic Tools
- The jcmd command, the jfr command, the java command
- OpenJDK: JDK Mission Control project
Threads & CPUHigh CPU: find the thread
From top -H to the nid in a thread dump: a repeatable way to name the code that is burning CPU.
MemoryTaking and reading a heap dump
Capture safely with jcmd, dump automatically on OOM, then read the dominator tree and paths to GC roots.
Garbage collectionReading GC logs with -Xlog
One line of configuration, the legacy flag mapping, and how to read pause lines for leaks and undersized heaps.