Why architecture matters here

JFR matters because it provides always-on, low-overhead production profiling and event recording -- so when a problem occurs, the history is already recorded -- invaluable for diagnosing production issues without reproducing them. Diagnosing production Java problems is hard: traditional profilers are often too heavyweight for continuous production use, so you're stuck reproducing issues (hard, time-consuming) or attaching a profiler after the fact (too late -- the issue already happened). JFR solves this: it's always-on (continuously recording -- low enough overhead for production) -- so when a problem occurs, the recent history is already recorded (the events leading up to it -- GC, allocation, locks, etc.) -- and you can dump and analyze it (seeing what happened -- without reproducing). This is invaluable for production diagnosis (post-incident forensics -- understanding what happened, since it was recorded). And beyond incidents, it provides continuous insight into the application's behavior (GC, allocation, contention -- for ongoing performance understanding). For operating Java applications in production (where diagnosing issues is crucial), JFR is an invaluable built-in tool, and understanding it (always-on recording, the events, the analysis) is understanding how to diagnose production Java effectively.

The always-on-because-low-overhead insight is the key enabler, and it's what makes JFR uniquely valuable. The reason JFR is so valuable is the combination of always-on and low-overhead. Always-on: JFR records continuously (not just when you attach a profiler) -- so the history is always being recorded (the events leading up to any problem already captured). This is crucial for diagnosis (you don't need to reproduce the problem or anticipate it -- the history is already there when it occurs). But always-on is only feasible if the overhead is low enough. Low overhead: JFR is engineered for very low overhead (typically under 1% -- the JVM efficiently recording the events -- built into the JVM, using efficient mechanisms) -- so it can be always-on in production (the <1% overhead acceptable for continuous production use -- versus a heavyweight profiler that would hurt production performance). The combination (always-on, enabled by the low overhead) is what makes JFR uniquely valuable: continuous recording in production (the history always available) at a cost low enough to always run it. This is different from traditional profilers (higher-overhead, so on-demand -- not always-on -- so the history isn't there when a problem occurs). Understanding the always-on-because-low-overhead insight (JFR always-on due to its <1% overhead -- so the history is always recorded) is understanding what makes JFR uniquely valuable for production.

And the ring-buffer-plus-dump-on-event reality is the operational core, giving you the history when you need it. JFR retains the recent history in a ring buffer (a bounded buffer -- keeping the recent events, aging out the old ones as new ones come -- so it holds a rolling window of recent history without unbounded growth). This means the recent history is always available (the ring buffer holding, say, the last few minutes/hours of events). When a problem occurs, you dump the recording (extracting the ring buffer's contents -- the recent history) -- so you get the events leading up to the problem (already recorded in the ring buffer). The dump can be triggered on demand (you manually dump when investigating) or on an event (automatically -- e.g., on an exception, a long GC pause, or a custom threshold -- so the history around a triggering event is automatically captured). This ring-buffer-plus-dump model (a rolling window of recent history, dumped on demand or on an event) is the operational core: the history is continuously recorded (the ring buffer) and captured when needed (the dump -- on demand or automatically on an event). So for an incident, you dump the recording and see the recent history (what happened) -- or set up automatic dumps on triggering events (capturing the history around them). Understanding the ring-buffer-plus-dump-on-event model (a rolling history, captured on demand or on an event) is understanding how JFR gives you the history when you need it.

Advertisement

The architecture: every piece explained

Top row: the idea and events. The idea: continuous event recording (the JVM continuously recording a stream of events -- like a black box recorder). JFR events: the recorded events -- GC (garbage collection -- pauses, cycles), allocation (object allocation -- allocation pressure, where objects are allocated), locks (lock contention -- where threads contend), I/O (file/socket I/O), thread states, exceptions, and more -- a rich event stream. Low overhead: JFR is very low overhead (typically <1% -- engineered into the JVM) -- so it can be always-on in production. Ring buffer: the recent history retained in a bounded ring buffer (a rolling window of recent events -- old ones aged out) -- so the recent history is always available.

Middle row: recording and analysis. Recordings: continuous recordings (always recording -- dumping on demand or on an event) -- and dumps (extracting the recording for analysis). JMC analysis: JDK Mission Control -- the analysis tool (visualizing the JFR events -- finding hotspots, GC issues, allocation pressure, lock contention -- from the recording). Async profiler + JFR: the async-profiler (which can emit JFR-format data -- low-overhead CPU/allocation profiling) combined with JFR -- producing flame graphs (visualizing where CPU time goes). Production diagnosis: JFR's forte -- post-incident forensics (dumping the recording after a problem -- seeing what happened, since the history was recorded) -- diagnosing production issues without reproducing.

Bottom rows: comparison and customization. vs sampling profilers: JFR (always-on, low-overhead -- the history always recorded) vs traditional sampling profilers (usually on-demand, higher-overhead -- attached when needed, the history not there beforehand) -- JFR's always-on advantage. Custom events: instrumenting your application with your own JFR events (recording application-specific events -- e.g., a business operation's timing) -- extending JFR with custom application insight. The ops strip: settings (the JFR settings -- which events to record, at what detail -- balancing the insight against the overhead; default/profile settings for different needs), retention (the ring-buffer size/duration -- how much recent history to retain -- and storing dumps -- for the available history), and automation (automating the dumps -- e.g., on exceptions, long GC pauses, or thresholds -- so the history around problems is automatically captured -- and integrating with monitoring).

Java Flight Recorder -- always-on, low-overhead profilingthe black box recorder built into the JVMThe ideacontinuous event recordingJFR eventsGC, allocation, locks, IOLow overhead<1% -- always-onRing bufferrecent history retainedRecordingscontinuous + dumpsJMC analysisMission ControlAsync profiler + JFRflame graphsProduction diagnosispost-incident forensicsvs sampling profilersalways-on vs on-demandCustom eventsinstrument your appOps — settings + retention + automationrecordanalyzeflamediagnosecomparecustomoperateoperateoperate
Java Flight Recorder: the JVM continuously records events (GC, allocation, locks, IO) into a ring buffer at <1% overhead -- so recent history is always available for diagnosis, dumped on demand or on an event.
Advertisement

End-to-end flow

Trace diagnosing a production incident with JFR. A Java service has a latency spike incident in production. Because JFR is always-on (recording continuously at <1% overhead), the recent history -- the events leading up to and during the spike (GC pauses, allocation, lock contention, thread states) -- is already recorded in the ring buffer. The team dumps the recording (extracting the ring buffer's recent history) and analyzes it in JDK Mission Control -- seeing what happened: e.g., a long GC pause (visible in the GC events) caused by allocation pressure (visible in the allocation events -- a burst of allocation), or lock contention (visible in the lock events). So they diagnose the incident from the recorded history (the events showing the cause -- the GC pause, the allocation, the contention) -- without needing to reproduce the incident (the history was already recorded). The always-on JFR (the history recorded before the incident) enabled the post-incident diagnosis (dump and analyze the recorded history) -- versus a traditional profiler (which wouldn't have been running, so the history wouldn't be there). JFR's always-on recording enabled the forensic diagnosis.

The overhead and analysis vignettes show the enablers. An overhead case: the team confirms JFR's low overhead (<1%) -- so they run it always-on in production (the continuous recording acceptable -- not hurting production performance) -- the low overhead enabling the always-on recording (versus a heavyweight profiler they couldn't run continuously). The low overhead enabled the always-on production use. An analysis case: the team uses JDK Mission Control to analyze the recording -- visualizing the events (GC timelines, allocation hotspots, lock contention, thread states) -- finding the performance issues (e.g., an allocation hotspot driving GC pressure) -- the JMC analysis turning the recorded events into actionable insight. And for CPU profiling, they use the async-profiler with JFR -- getting flame graphs (visualizing where the CPU time goes -- finding CPU hotspots). The analysis tools turned the recording into insight.

The automation and custom-events vignettes complete it. An automation case: the team automates the JFR dumps -- configuring automatic dumps on triggering events (e.g., on a long GC pause, an exception, or a latency threshold) -- so the history around any triggering event is automatically captured (a dump saved when the event occurs -- the history preserved for analysis) -- so they don't miss the history of transient problems (automatically captured). The automation captured the history around problems. A custom-events case: the team adds custom JFR events to their application (instrumenting key operations -- e.g., recording a business operation's timing as a custom JFR event) -- so the JFR recording includes application-specific insight (their custom events alongside the JVM events) -- extending JFR with application-level diagnosis. The custom events added application insight. The consolidated discipline the team documents: use JFR for always-on, low-overhead production profiling and event recording (the history always recorded -- so problems can be diagnosed from the recorded history, without reproducing), leverage the low overhead (<1% -- enabling always-on production use), use the ring-buffer-plus-dump model (a rolling recent history, dumped on demand or on an event), analyze with JDK Mission Control (and async-profiler for flame graphs -- turning the events into insight), automate dumps on triggering events (capturing the history around problems), add custom events (application-specific insight), tune the settings (events, detail -- balancing insight against overhead) and retention, and use it for production diagnosis (post-incident forensics) -- because JFR is the JVM's built-in, always-on, low-overhead diagnostic recorder (the history always available -- so production issues can be diagnosed from the recorded history without reproducing them), invaluable for understanding production behavior and diagnosing incidents.