rgoussu@goussu: ~/library/java/jdk-and-tools
~/library/java/jdk-and-tools cat jfr-and-jmc.md

JDK Flight Recorder & Mission Control

# Always-on, low-overhead JVM event recording (JFR) and its analysis GUI (JMC) — the built-in production profiling stack.

Conceptsaved 2026-08-09 #java#jvm#tooling#profiling#observability#diagnostics

Overview

JDK Flight Recorder (JFR) is an event-recording engine built into HotSpot: hundreds of typed events — GC phases, allocation, method samples, lock contention, I/O, class loading, safepoints — are collected into ring buffers with overhead low enough (targeted ~1% with default settings) to leave on in production. Mission Control (JMC) is the companion desktop application that analyses the resulting .jfr recordings. Both were commercial Oracle features until open-sourced in JDK 11 (JFR, JEP 328) and as the separate JMC project; together they are the "black box" flight recorder metaphor made literal — when something goes wrong, the data was already being recorded.

Key points

  • Low overhead by design: events are written to thread-local buffers, flushed to an in-memory ring or disk repository in a binary, self-describing format; sampling-based method profiling avoids safepoint bias better than JVMTI-based profilers of old.
  • Two standard settings sets: default (~<1%, always-on safe) and profile (~2%, adds allocation sites and finer sampling) — .jfc files you can copy and customise (JMC has a template editor).
  • Starting at launch: -XX:StartFlightRecording=duration=120s,settings=profile, filename=app.jfr; or continuous: maxage=12h,maxsize=500m,name=main with no filename, then dump on demand.
  • Controlling live: jcmd <pid> JFR.start/JFR.dump/JFR.check/JFR.stop — dump the last N minutes from a continuous recording after an incident: jcmd 4242 JFR.dump name=main maxage=10m filename=incident.jfr.
  • jfr CLI tool: jfr print, jfr summary, jfr view <view> file.jfr (Java 21 added useful built-in views like hot-methods, gc-pauses) for headless triage without JMC.
  • Custom events: the jdk.jfr API — subclass jdk.jfr.Event, annotate, commit() — puts your business/framework events on the same timeline as JVM events; frameworks lean on this (Spring's ApplicationStartup has a FlightRecorderApplicationStartup variant, Quarkus and Micronaut publish framework events similarly).
  • Streaming (JEP 349, Java 14): RecordingStream subscribes to events in-process (or from the disk repository of another process) as they happen — the bridge from JFR to live metrics dashboards without waiting for a dump.
  • JMC analysis: automated-analysis page (rule engine flags problems in plain language), method profiling flame graph, allocation by site, lock contention ("Java Blocking"), GC pause breakdowns, memory-leak hunting via OldObjectSample reference chains.
  • Not in the JDK: JMC downloads separately (Oracle, Adoptium/Eclipse builds); JFR itself needs nothing installed — every JDK 11+ (and 8u262+ backport) has it.

Details

Recording lifecycle in practice

Situation Move
Always-on production insurance -XX:StartFlightRecording=maxage=12h,maxsize=1g,name=main at launch
Incident just happened jcmd <pid> JFR.dump name=main maxage=15m filename=inc.jfr
Reproduce a slow endpoint jcmd <pid> JFR.start settings=profile duration=2m filename=slow.jfr
Headless first look jfr summary inc.jfr, jfr view hot-methods inc.jfr
Real analysis open in JMC → Automated Analysis → drill into Method Profiling / GC / Locks

JFR vs async-profiler

  • async-profiler samples via perf/itimer signals and reads native + Java stacks, so it sees kernel/native frames JFR's Java-oriented sampler misses, and produces flame graphs directly; it can also profile allocations and locks, and can even emit JFR-format output.
  • JFR wins on breadth (GC, I/O, safepoints, class loading, custom events on one timeline), on being pre-installed and supported everywhere, and on continuous always-on recording.
  • Sensible split: JFR as the always-on baseline and incident recorder; async-profiler when you need precise CPU flame graphs including native code (e.g. chasing the last percent in a 1BRC-style optimisation). Since JDK 17+ JFR's own CPU sampling has improved, and JMC renders flame graphs too, so the gap narrows.

Reading a recording — where to look first

  1. Automated Analysis — triage, often names the problem outright.
  2. Java Application → Method Profiling — CPU hot spots (check sample count is meaningful).
  3. Memory → Allocation — allocation pressure by site; feeds GC tuning.
  4. Java Application → Locks — monitor contention with stack traces.
  5. JVM Internals → Garbage Collections / Safepoints — pause culprits that are not GC.

Examples

# continuous recording + post-incident dump
java -XX:StartFlightRecording=name=main,maxage=12h,maxsize=1g -jar app.jar
jcmd $(pgrep -f app.jar) JFR.dump name=main maxage=10m filename=/tmp/incident.jfr
jfr view hot-methods /tmp/incident.jfr
@Name("shop.OrderPlaced")
@Label("Order placed")
class OrderPlaced extends jdk.jfr.Event {
    @Label("Order id") long orderId;
    @Label("Total cents") long totalCents;
}
// ... new OrderPlaced() ... event.commit();

// streaming: live consumption in-process
try (var rs = new jdk.jfr.consumer.RecordingStream()) {
    rs.enable("jdk.GCPhasePause");
    rs.onEvent("jdk.GCPhasePause", e ->
        System.out.println(e.getDuration().toMillis() + " ms pause"));
    rs.startAsync();
}

Related

  • JDK & tools — parent catalogue of the JDK toolchain.
  • jcmd — the runtime remote-control for recordings (JFR.*).
  • jconsole — live JMX values vs JFR's recorded events.
  • jstat — the zero-setup counter view JFR largely supersedes.
  • jmap — heap dumps when OldObjectSample is not enough.
  • One Billion Row Challenge — the kind of optimisation work where JFR/async-profiler earn their keep.
  • Performance engineering — profiling as part of a disciplined performance method.