Production

Java Flight Recorder and JDK Mission Control: recording production safely

Production · Published 2026-10-24

An orange flight data recorder on display, with the words Flight Data Recorder painted on its side
Like its aviation namesake, JFR keeps a rolling record so the moments before an incident can be replayed. Photo: André Gustavo Stumpf, Wikimedia Commons, CC BY 2.0.

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/:

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

QuestionEvents
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 timejdk.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

Sources