Analyzing Recordings¶
cjfr queries .cjfr files directly; no inflation needed. All analysis
commands accept the same filtering flags, so you can zero in on the
30-second GC storm you care about before deciding whether to inflate the full recording.
Commands at a glance¶
| Command | Purpose |
|---|---|
cjfr summary |
Aggregate stats: event counts, GC summary, allocation rate |
cjfr view <VIEW_OR_EVENT> <FILE...> |
Tabular view of a named view or one event type |
cjfr print <FILE...> |
Print raw events in jfr print text or JSON format |
cjfr inflate |
Convert to JFR for JDK Mission Control, Firefox Profiler, jfr-query, async-profiler, etc. |
All four accept the same filter flags described below.
Time-range filtering¶
Narrow the window you care about with --start, --end, or --duration.
Timestamps are in local time unless you include an explicit offset.
| Flag | Accepted formats | Example |
|---|---|---|
--start |
yyyy-MM-dd HH:mm:ss, yyyy-MM-ddTHH:mm:ss, ISO-8601 with timezone |
--start="2024-05-24 12:07:00" |
--end |
same as --start |
--end="2024-05-24 12:09:00" |
--duration |
1h30m, 5m, 30s, 500ms, 100us |
--duration=2m |
Combine --start + --end or --start + --duration (or --end + --duration). Don't pass all three.
Use cjfr summary --short recording.cjfr to find the recording's start time.
# Summary of a 2-minute window
cjfr summary --start="2024-05-24 12:07:00" --duration=2m recording.cjfr
# View heap summaries in that window
cjfr view --start="2024-05-24 12:07:00" --end="2024-05-24 12:09:00" \
jdk.GCHeapSummary recording.cjfr
# Inflate just that window for Mission Control
cjfr inflate --start="2024-05-24 12:07:00" --duration=2m \
recording.cjfr slice.jfr
GC percentile filter¶
Focus on the worst GC pauses and the application activity that caused them.
--gc-percentile=N keeps only events that fall within the --gc-percentile-context
window (default 1 minute) around every GC whose pause duration is at or above
the Nth percentile. Pass 0 to disable (default).
- With
cjfr summary, this changes the event-count table: you see counts of what happened around the worst pauses. The standard GC Summary section is still shown when the input is a single file. - With
cjfr inflate, this produces a.jfrcontaining only those windows; much smaller than a full inflation and fast to open in JMC. - With
cjfr view, it restricts the event listing to those windows.
# Keep only events near the slowest 10% of GC pauses (≥ 90th percentile)
cjfr summary --gc-percentile=90 recording.cjfr
# Widen the context window to 2 minutes around each qualifying GC
cjfr summary --gc-percentile=90 --gc-percentile-context=2m recording.cjfr
# Inflate only those high-pause windows to JFR
cjfr inflate --gc-percentile=95 recording.cjfr pauses.jfr
--gc-percentile-context defaults to 1m: the time window before and after
each qualifying GC pause to include. A smaller value (e.g. 15s) gives
tighter slices; a larger one (e.g. 5m) captures longer allocation patterns that
build up before the pause.
Event-type filtering¶
Pass --events to include only specific JFR event types. Accepts a
comma-separated list, and is repeatable.
For a full reference of available JFR event types and their fields, see JFR Events.
# Summary counting only GC events
cjfr summary --events=jdk.GarbageCollection,jdk.GCHeapSummary recording.cjfr
# Inflate to a GC-only JFR (much smaller than full inflation)
cjfr inflate --events=jdk.GarbageCollection,jdk.GCHeapSummary,jdk.G1HeapSummary \
recording.cjfr gc-only.jfr
# Repeatable form (same result):
cjfr inflate --events=jdk.GarbageCollection --events=jdk.GCHeapSummary \
recording.cjfr gc-only.jfr
Useful event groups for GC analysis:
| Goal | Event types |
|---|---|
| GC pauses only | jdk.GarbageCollection, jdk.GCPhasePause |
| Heap sizing | jdk.GCHeapSummary, jdk.G1HeapSummary, jdk.MetaspaceSummary |
| Allocation pressure | jdk.ObjectAllocationInNewTLAB, jdk.ObjectAllocationOutsideTLAB, jdk.ObjectAllocationSample |
| Full GC picture | jdk.GarbageCollection, jdk.GCHeapSummary, jdk.TenuringDistribution, jdk.GCReferenceStatistics, jdk.GCCPUTime |
Collector-specific pause fields in jdk.GarbageCollection: the pause field name
varies by GC algorithm. When using cjfr view --json or scripting against JSON output:
| Collector | Pause field(s) | Unit |
|---|---|---|
| G1GC | longestPause, sumOfPauses |
nanoseconds |
| ZGC | duration |
nanoseconds |
| Shenandoah | duration |
nanoseconds |
| Serial / Parallel | longestPause, sumOfPauses |
nanoseconds |
cjfr summary --json always reports .gc.p95Micros and .gc.maxMicros in microseconds regardless of collector, derived from the collector's pause field.
Working with multiple files¶
All files are merged in time order; the normal way to work with a rotating recording set.
GC Summary is single-file only
cjfr summary only produces the dedicated GC Summary section when querying
a single file. When multiple files are passed, the event count table is
merged across all files, but the GC-specific summary section is omitted.
Workarounds:
- Run
summary --shorton the most recent rotation file per host; it is usually representative. - Run
summary --jsonon each file individually and aggregate.gc.p95Micros/.gc.maxMicros(values in microseconds). - Or
inflatethe multi-file set to a single.jfrand re-runsummary --shorton that single output.
For cjfr summary, pass all files as positional arguments (glob expansion works too):
# Summary across a whole day of rotating recordings
cjfr summary rec_0.cjfr rec_1.cjfr rec_2.cjfr
cjfr summary rec_*.cjfr
For cjfr inflate, all input files come first as positional arguments, with the output file (.jfr) last. For cjfr view, the view or event name comes first (mirroring the JDK jfr view), followed by one or more input files:
# Inflate multiple files into a single JFR
cjfr inflate rec_0.cjfr rec_1.cjfr rec_2.cjfr merged.jfr
# Combine multi-file with time range: extract 5-minute window across the set
cjfr inflate --start="2024-05-24 03:00:00" --duration=5m \
rec_0.cjfr rec_1.cjfr window.jfr
# View one event type across several files (name first, then the files)
cjfr view jdk.GarbageCollection rec_0.cjfr rec_1.cjfr rec_2.cjfr
summary output modes¶
cjfr summary recording.cjfr # default: header + event table + GC/alloc summary
cjfr summary --short recording.cjfr # header + GC/alloc summary only (no event table)
cjfr summary --full recording.cjfr # adds EventWriteTree and per-type byte statistics
cjfr summary --json recording.cjfr # machine-readable JSON
cjfr summary --limit=10 recording.cjfr # show only the 10 largest event types
cjfr summary --flamegraph storage.html recording.cjfr # storage flamegraph by event type
The --flamegraph output shows byte distribution across event types, not
CPU time; useful for understanding which event types dominate file size.
view output and formatting¶
# Show all jdk.GarbageCollection events
cjfr view jdk.GarbageCollection recording.cjfr
# Limit to first 20
cjfr view --limit=20 jdk.GarbageCollection recording.cjfr
# JSON output (suitable for piping to jq)
cjfr view --json jdk.GarbageCollection recording.cjfr | jq '.[] | .gcId'
# Combine with time range
cjfr view --start="2024-05-24 12:07:00" --duration=30s --limit=50 \
jdk.GCHeapSummary recording.cjfr
# Render a JDK named view directly (natively when possible, else via `jfr view`)
cjfr view gc-pauses recording.cjfr
Controlling column width and truncation¶
--width=<n> overrides the terminal width used for table layout. When cells are
too wide to fit, --truncate controls which end is kept:
# Keep the end of long cell values (useful for fully-qualified class names)
cjfr view --width=120 --truncate=beginning jdk.GarbageCollection recording.cjfr
# Keep the start of long cell values (default)
cjfr view --width=120 --truncate=end jdk.GarbageCollection recording.cjfr
--truncate=beginning (alias begin) keeps the tail of long strings — most useful
for fully-qualified class names in stack traces, where the class leaf is at the end.
--truncate=end (the default) keeps the head.
Did-you-mean suggestions¶
If you mistype a view or event name, cjfr view suggests the closest match:
Named views are also included in suggestions, so hot-method will suggest hot-methods.
Named views¶
cjfr view is a drop-in replacement for the JDK jfr view command and supports
all of its named views (gc-pauses, hot-methods, allocation-by-site,
exception-by-type, jvm-information, …). The list of views is not bundled or
hard-coded: cjfr reads the running JVM's own view.ini from the jrt: runtime
image (jdk/jfr/internal/query/view.ini) and evaluates each view's query natively,
so the set of available views always matches the JDK you run cjfr on.
cjfr view hot-methods recording.cjfr
cjfr view allocation-by-site recording.cjfr
cjfr view --width=120 gc-configuration recording.cjfr
Any view that can't be evaluated natively — or running on a pre-21 JDK where the
view.ini isn't available — falls back automatically to delegating to
$JAVA_HOME/bin/jfr view, so every view the installed JDK offers keeps working.
Rendering a named view straight from a .cjfr is also faster than opening the
original .jfr (measured ~2–3× on a 253 MB gc_details recording), since only
the event types the view needs are read.
print output and formatting¶
cjfr print is a drop-in for the JDK jfr print command and renders events
in the same text format:
# Print all events
cjfr print recording.cjfr
# Filter by event type or glob pattern
cjfr print --events GCPhaseParallel recording.cjfr
cjfr print --events "jdk.GC*" recording.cjfr
cjfr print --events "CPULoad,GCHeapSummary" recording.cjfr
# Filter by JFR category annotation (comma = OR, globs supported)
cjfr print --categories GC recording.cjfr
cjfr print --categories "GC,Profiling" recording.cjfr
# Full-precision output: nanosecond timestamps, exact byte values, unrounded floats
# Use when the default human-readable rounding obscures the data you care about
cjfr print --exact recording.cjfr
# Limit stack trace depth
cjfr print --stack-depth 5 recording.cjfr
# JSON output
cjfr print --json recording.cjfr
# Also works on raw .jfr files
cjfr print recording.jfr
--no-reconstitution¶
The reduced condenser config combines some event types into buckets
(e.g., many ObjectAllocationSample events become a single aggregated entry).
By default, summary and view expand these back into approximate individual
events. Pass --no-reconstitution to skip expansion and read the raw combined
events directly. Useful when you want aggregate metrics rather than reconstructed
individual events.
This has no effect on files produced with lossless or default.
Integrity checks¶
cjfr inflate and cjfr summary verify a whole-file CRC32 (stored in the file's
footer) before reading. If a .cjfr file has been corrupted — silent bit-rot,
a truncated copy, a bad transfer — the command aborts with an integrity error
rather than producing garbage output.
Verification only runs for direct file inputs. It is skipped (and noted) for
inputs that cannot be re-read, such as a .cjfr entry inside a ZIP.
If a file is corrupted but you still want to salvage what you can, bypass the check:
cjfr summary --ignore-integrity recording.cjfr
cjfr inflate --ignore-integrity recording.cjfr out.jfr
Older files written before the CRC feature carry no checksum and are read without a warning.