Definition
- JFR (Java Flight Recorder) is a profiling and diagnostics framework built into the JVM since Java 11 (free).
- Low overhead: approximately 1-2% performance impact, designed for always-on production monitoring.
- Records: GC pauses, memory allocations, thread contention, CPU hotspots, I/O latency, class loading, and exceptions.
- Start recording: command-line flag
-XX:StartFlightRecording=duration=60s,filename=app.jfror at runtime withjcmd.JFR.start - Analyze: open the
.jfrfile in JDK Mission Control (JMC) or use thejfrcommand-line tool. - Custom events: extend
jdk.jfr.Eventto record domain-specific events like settlement batch timing.
JFR is like the bank's security camera system that records everything: customer flow, employee activity, system access. It runs continuously with minimal impact on daily operations. When something goes wrong (a performance incident), you review the recording to find the cause. Unlike security cameras, JFR costs almost nothing in resources.
Understanding the Concepts
What does JFR record?
JFR continuously records JVM events into a compact binary .jfr file. The events include:
- GC events: pause times, heap sizes before and after, GC algorithm details.
- Memory allocation: which threads allocated the most memory, allocation rates.
- Thread contention: lock wait times, thread states, blocked threads.
- CPU profiling: method sampling to identify hot code paths.
- I/O latency: file and socket read/write durations.
- Exceptions: every exception thrown, even if caught.
How do you start a recording?
# Start with the application
java -XX:StartFlightRecording=duration=60s,filename=app.jfr \
-jar banking-service.jar
# Start on a running process
jcmd <pid> JFR.start duration=60s filename=settlement.jfr
# Stop a running recording
jcmd <pid> JFR.stop name=1
How do you create custom application events?
package com.bank.monitoring;
import jdk.jfr.*;
@Name("com.bank.SettlementBatch")
@Label("Settlement Batch")
@Description("Records settlement batch execution")
public class SettlementEvent extends Event {
@Label("Batch ID")
String batchId;
@Label("Transaction Count")
int transactionCount;
@Label("Total Amount")
long totalAmountPaise;
}
// Usage:
SettlementEvent event = new SettlementEvent();
event.batchId = "BATCH-20240115";
event.transactionCount = 1500;
event.totalAmountPaise = 75000000L;
event.begin();
// ... execute batch ...
event.commit(); // records duration automatically
How do you analyze recordings?
Use JDK Mission Control (JMC) for visual analysis or the jfr command-line tool.
# Print summary of events
jfr summary app.jfr
# Print specific event type
jfr print --events jdk.GarbageCollection app.jfr
# Print custom events
jfr print --events com.bank.SettlementBatch app.jfr
How is JFR different from traditional profiling?
| Feature | JFR | Traditional Profiler |
|---------|-----|---------------------|
| Overhead | ~1-2% | 5-50% |
| Production safe | Yes | Usually not |
| Always-on | Designed for it | Attach-on-demand |
| Built-in | Part of JDK | External tool |
| Custom events | Yes (extend Event) | Varies |
What about JFR Streaming API (Java 14+)?
JFR data can be streamed in real-time for continuous monitoring dashboards.
import jdk.jfr.consumer.RecordingStream;
try (var stream = new RecordingStream()) {
stream.enable("jdk.GarbageCollection").withThreshold(java.time.Duration.ofMillis(100));
stream.onEvent("jdk.GarbageCollection", event -> {
System.out.println("GC pause: " + event.getDuration().toMillis() + "ms");
});
stream.startAsync(); // runs in background
}
What are common mistakes?
// WRONG: leaving JFR recordings running indefinitely without a duration limit
java -XX:StartFlightRecording=filename=app.jfr -jar banking-service.jar
// The .jfr file grows unbounded!
// CORRECT: set a duration or maxSize
java -XX:StartFlightRecording=duration=60s,filename=app.jfr -jar banking-service.jar
java -XX:StartFlightRecording=maxsize=500m,filename=app.jfr -jar banking-service.jar
What should a senior engineer know?
- JFR complements metrics and traces: metrics tell you SOMETHING is slow, JFR tells you WHY at the JVM/allocation/lock level.
- The programmatic API (
jdk.jfr.Recording) lets you start and stop recordings in integration tests to capture performance baselines. - JFR data can be streamed in real-time with the JFR Streaming API (Java 14+) for continuous monitoring dashboards.
- In banking, JFR recording of GC pauses during peak trading hours helps tune GC settings for latency-sensitive services.
Visual Overview
┌───────────────────────────────────────┐
│ Java Application │
│ │
│ ┌─────────────────────────────────┐ │
│ │ JFR (always-on, ~1% overhead) │ │
│ │ │ │
│ │ Records: │ │
│ │ ├── GC pauses │ │
│ │ ├── Memory allocations │ │
│ │ ├── Thread contention │ │
│ │ ├── CPU hotspots │ │
│ │ ├── I/O latency │ │
│ │ └── Custom events │ │
│ └──────────────┬──────────────────┘ │
└─────────────────┼─────────────────────┘
│
▼
app.jfr file
│
▼
JDK Mission Control (JMC)
Key Takeaway
Java Flight Recorder is a production-safe profiling tool built into the JVM since Java 11. It records GC pauses, memory allocations, thread contention, CPU hotspots, and I/O latency with approximately 1-2% overhead, making it safe for always-on monitoring.
Start recording with -XX:StartFlightRecording on the command line or jcmd on a running process. Analyze the .jfr file with JDK Mission Control for visual analysis. Create custom application events by extending jdk.jfr.Event to record domain-specific metrics like settlement batch timing.
At National Bank of India, the settlement service enables JFR during peak hours to capture GC pauses and lock contention. Mission Control reveals that a BigDecimal allocation hotspot causes 200ms GC pauses, guiding optimization.
Remember one sentence: JFR is the built-in, always-on, low-overhead profiler that tells you WHY your application is slow in production.
---