How Java Flight Recorder records JVM profiles with low overhead
See how JFR filters typed events, writes private buffers, samples stacks, reuses metadata, and preserves recent history in recording chunks.
The whole story in 5 lines
JFR stays light by filtering early, writing privately, sampling sparsely, reusing metadata, and rotating complete chunks.
- Settings reject unneeded events before their data enters the recording path.
- Private write lanes let application threads append without sharing one hot cursor.
- Periodic snapshots trade perfect detail for a useful profile with bounded work.
- Shared IDs keep repeated stacks and methods from bloating every event record.
- Self-contained chunks turn compact records into a recoverable rolling history.
Meet the pieces JFR moves
Java Flight Recorder watches a running JVM through small typed records. Let us meet the pieces before following their journey.
An event is one timestamped fact, such as a long lock wait, and its type tells tools which fields belong inside it.
A threshold is an early gate that rejects short duration events, which avoids paying to record details that the current configuration does not need.
A thread-local buffer is a private strip of memory. One Java thread appends event bytes using its own write cursor.
A stack sample is a periodic snapshot of where a thread is executing, so repeated snapshots can reveal which methods stay busy.
A reference pool stores repeated stacks and method metadata once. Those compact records will later need a durable container.
A chunk bundles the event stream with the information needed to parse it, while a rolling recording can discard older chunks as limits are reached.
These six pieces form one low-overhead path, so let us begin with the gate that decides whether an event enters at all.
Useful events pass a cheap gate
Now that we know the cast, we can follow three illustrative duration events. A setting decides whether each one is worth recording.
The enabled setting is the first check, because a disabled event type can stop before JFR gathers or writes its optional details.
The threshold then compares each event duration with the configured minimum, which keeps short events outside the buffer path.
The active threshold is 5 milliseconds, and the three events last 2, 9, and 28 milliseconds. Which records will cross the gate?
The accepted records move into a compact stream while the shorter record falls away, making the result of the gate visible before any buffer work begins.
Switch the Threshold control through every option. Compare which duration markers enter the recording and which disappear before buffer work.
Early filtering protects the rest of the recording path from avoidable work. Next, each accepted record needs a low-contention place to land.
Every thread writes in its own lane
The event gate produced compact records, and now each visible row belongs to one Java thread with its own empty memory strip and cursor.
One thread appends a record by advancing only its own cursor, so no unrelated thread needs to acquire that cursor first.
Several threads can append during the same beat because their cells are separate. Their cursors advance in parallel instead of taking turns.
One private strip is now full, but the application thread still has more events to record. What should happen to that completed strip?
The full strip detaches into global history while a fresh empty strip appears in its lane, so recording can continue after a brief handoff.
Switch the Write path control between Private lanes and Shared cursor. Compare independent appends with one queued cursor.
Private ownership keeps the common append path off busy application threads. Next, periodic stack samples will join the ordinary event stream.
Snapshots reveal the hot method
★ If you remember one thing · A few periodic stack snapshots make the repeatedly running method stand out without tracing every call.
Private buffers can accept ordinary events, and profiling adds periodic snapshots that ask where a running thread is executing at selected moments.
The worked timeline contains a short parser, a long checkout loop, and a short response writer, with width representing time spent in each method.
Sample needles land at a fixed period. Each needle adds one stack reference instead of tracing every method entry and exit.
These snapshots capture only a few moments from the execution window. Which method should still lead after the sample hits are counted?
Most needles land inside the long checkout span, so its computed profile bar grows longest even though JFR never traced every call.
Switch the Sample period control. Compare Checkout.run as a clear leader with the sparse three-way tie across methods.
Sampling trades complete tracing for bounded recurring work and a useful statistical signal for later diagnosis. Next, repeated stack details must become compact references.
Repeated stacks become small references
The sampler produced stack-bearing events, but many snapshots point to the same methods, so repeating every frame would waste recording space.
In repeated form, each event carries another visible tower of stack frames even when that tower matches one already recorded.
Several events carry the same stack shape, so storing every tower repeats identical structure. How should matching samples share that information?
The pool keeps each unique stack once and assigns it an ID, while sample events shrink into tokens that carry the matching reference.
The repeated towers collapse into one shared pool and a row of small IDs, so the saved structural units are visible beside the records.
Switch the Encoding control. Compare repeated frame units with the compact pool and its event references.
Reference pooling preserves stack identity while removing repeated structure from individual events throughout the recording. Next, those compact records need a self-contained file container.
Chunks preserve a rolling history
The compact records now need a durable container, so JFR writes a chunk with a header and an event stream that carries parsing information.
Metadata records describe event types and fields, while checkpoint records carry shared constants such as classes, methods, threads, and stack traces.
Ordinary events and execution samples share that stream. Their small references resolve through checkpoint data stored inside the same chunk.
A continuous recording keeps producing chunks after its history limit is full. Which files should remain when the newest chunk is sealed?
The newest sealed chunk joins the right edge while the oldest falls away, leaving a chronological window whose remaining chunks can still be parsed.
Adjust the Chunks kept control across its range. Compare whether trigger and pre-trigger context survive rotation for later diagnosis.
Chunk rotation bounds storage without breaking the parseability of retained history. Now let us connect every low-overhead decision in one final map.
The complete low-overhead path
We started at the event gate, where enabled settings and thresholds prevent unwanted duration events from entering the recording path.
We then gave every application thread a private buffer lane, so concurrent producers do not fight over one shared append cursor.
Periodic stack snapshots turned a computed execution timeline into a profile. The hottest method emerged without tracing every call.
Shared reference pools stored repeated stacks and methods once, while compact IDs carried those relationships into each matching event.
Filtering, private writes, sampling, and deduplication all reduce foreground work. Complete recording chunks preserve those compact observations after the moment passes.
Self-contained chunks connect every earlier piece into a rolling recording, and the worked numbers came from one deterministic model backed by official JFR specifications.
JFR remains practical in production because it filters early, writes privately, samples sparsely, reuses metadata, and rotates complete chunks.
The whole story in 5 lines
JFR stays light by filtering early, writing privately, sampling sparsely, reusing metadata, and rotating complete chunks.
- Settings reject unneeded events before their data enters the recording path.
- Private write lanes let application threads append without sharing one hot cursor.
- Periodic snapshots trade perfect detail for a useful profile with bounded work.
- Shared IDs keep repeated stacks and methods from bloating every event record.
- Self-contained chunks turn compact records into a recoverable rolling history.






