1. Overview#
1.1. JFR Event Collection Overview#
JFR (Java Flight Recorder) is a low-overhead performance monitoring and diagnostic tool provided by the Java platform. It was originally a commercial feature of Oracle JDK, and since JDK 11, it has been integrated into OpenJDK as an open-source feature. The corresponding JEP and JBS are:
JFR records various information during JVM runtime through an event collection mechanism, including:
- GC Events: GC type, duration, amount of memory reclaimed, etc.
- Thread Events: Thread creation, destruction, state changes, etc.
- Method Execution Events: Method invocations, execution time, sampling information, etc.
- Object Allocation Events: Object allocation location, size, type, etc.
- System Events: CPU usage, memory usage, network I/O, etc.
- Custom Events: Applications can define and record custom events
The core advantage of JFR lies in its low-overhead characteristics. Through carefully designed event collection mechanisms, JFR can run continuously in production environments, with performance impact typically less than 1% (Note: In practice, proper configuration is required to achieve low overhead while maintaining continuous collection for problem diagnosis).
Traditional performance monitoring tools typically use sampling or instrumentation approaches, but these methods often introduce significant performance overhead. JFR is a low-overhead JVM monitoring mechanism that can be continuously enabled, particularly suitable for post-mortem analysis of performance bottlenecks and critical issues. In production environments, JFR’s core value is reflected in the following aspects:
- Emergency Response When Issues Occur: When a JVM process encounters problems, the primary task is to quickly restore business operations (restarting instances, taking down problematic instances, scaling up, etc.), rather than immediately analyzing the problem. At this point, tools that can continuously record and enable post-mortem analysis are needed.
- Time Window of Problem Scenarios: When problems occur, the critical time point when the problem happened has often already been missed. Even if instances are taken offline to preserve the scene, using real-time analysis tools (such as JVisualVM, Arthas) may no longer be able to obtain the scene data from when the problem occurred.
- JVM Unresponsive Scenarios: Certain severe problems (such as OOM, deadlocks causing thread blocking, etc.) may cause the JVM to be unable to respond to external diagnostic requests (JMX, jcmd). At this point, real-time analysis tools completely fail. Since JFR is a continuous recording mechanism, even if the JVM encounters problems, it can preserve historical data before the problem occurred. For specific cases, refer to: 6. How to Quickly Locate Java Heap OOM Through JFR: Practice and Underlying Principles
The following is a comparison between several common tools and JFR:
1.1.1. JVisualVM#
JVisualVM is a graphical monitoring tool bundled with the JDK that obtains JVM runtime data through JMX connections.
- Advantages: Graphical interface, easy to use; supports heap dump and thread dump analysis
- Disadvantages:
- Based on JMX connections + Agent instrumentation sampling, with high performance overhead (10~20%)
- Collection may be interrupted when JVM is under high pressure in production environments
- Cannot continuously record historical data, not suitable for post-mortem analysis
- Comparison with JFR: JFR has lower overhead (<5%), supports continuous recording of historical data, and is more suitable for continuous operation in production environments and post-mortem analysis
1.1.2. Arthas#
Arthas is an open-source Java diagnostic tool from Alibaba that implements dynamic monitoring through bytecode enhancement.
- Advantages: No need to restart the application; supports dynamic code modification; provides rich diagnostic commands
- Disadvantages:
- Many features are based on bytecode enhancement, with significant performance overhead (10~20%)
- Not suitable for long-term enabling, poses security risks
- Requires the JVM to be able to respond to external requests, not suitable for post-mortem problem analysis
- Comparison with JFR: JFR has lower overhead and is more suitable for continuous operation in production environments; Arthas is more suitable for temporary diagnosis and problem troubleshooting
1.1.3. OpenTelemetry / Skywalking#
OpenTelemetry is a distributed tracing and APM (Application Performance Monitoring) standard, and Skywalking is a distributed tracing tool implemented based on OpenTelemetry, implemented through Java Agent instrumentation.
- Advantages: Supports distributed tracing; provides complete call chain analysis; supports multiple languages
- Disadvantages:
- Based on bytecode instrumentation, with significant performance overhead (5-15%)
- Requires additional storage and computing resources, full reporting costs are very high (much higher than log costs)
- Focuses on application-layer call chain analysis, not suitable for JVM internal event monitoring
- Comparison with JFR: JFR focuses on JVM internal events with lower overhead; APM tools focus on application-layer call chains, and the two can be used complementarily
1.1.4. Practical Usage Recommendations#
In actual production environments, it is recommended to adopt a layered monitoring strategy, combining the advantages of multiple tools:
1. OpenTelemetry + APM Tools (such as Skywalking):
- Metrics Collection: Use Prometheus + Grafana for metric monitoring and alerting
- Traces Collection: Use Jaeger or Skywalking for call chain tracing
- Since full reporting costs are very high, it is recommended to adopt a sampling strategy (such as 5% sampling rate)
- Mainly used for alerting and initial problem localization (locating problematic microservice instances and approximate locations)
- If Traces data is insufficient to locate the root cause, then check the corresponding instance’s JFR data 2. Continuous JFR Collection:
- Enable JFR on each microservice instance, using JFR’s built-in mechanism to save JFR files from the last 3 days in the local temporary file directory
- When problems occur, before taking the JVM instance offline, upload all JFR files from the corresponding instance’s temporary file directory to centralized storage (such as S3, OSS) for post-mortem analysis
- JFR data is used for in-depth analysis of JDK + JVM internal events, locating performance bottlenecks and root cause issues:
- For example, all lock issues, including distributed locks, can be analyzed through JFR’s lock-related events (Monitor-related events, Thread Sleep, Thread Park, etc.)
- JFR can also help analyze GC behavior, thread states, hot method execution, etc.
1.2. JFR Configuration Overview#
flowchart LR
A[JFR Configuration System] --> B[Global Configuration]
A --> C[Recording Level Configuration]
B --> B1[-XX:+FlightRecorder
Introduced in JDK 11
Function: Enable/Disable JFR]
B --> B2[Global Options
Specified via -XX:FlightRecorderOptions=
or modified via jcmd JFR.configure
or modified via JMX]
B2 --> B21[repository
Introduced in JDK 11
Default: Temporary directory
Function: Repository path]
B2 --> B22[globalbuffersize
Introduced in JDK 11
Default: 512k
Function: Global buffer size]
B2 --> B23[numglobalbuffers
Introduced in JDK 11
Default: 20
Function: Number of global buffers]
B2 --> B24[memorysize
Introduced in JDK 11
Default: 10m
Function: Total memory limit for JFR]
B2 --> B25[threadbuffersize
Introduced in JDK 11
Default: 8k or OS pageSize
Function: Thread buffer size]
B2 --> B26[maxchunksize
Introduced in JDK 11
Default: 12m
Function: Maximum chunk size]
B2 --> B27[samplethreads
Introduced in JDK 11
Deprecated in JDK 19
Default: true
Function: Whether to enable thread sampling]
B2 --> B28[stackdepth
Introduced in JDK 11
Default: 64
Function: Stack trace depth]
B2 --> B29[retransform
Introduced in JDK 11
Default: true
Function: Whether to allow JVMTI retransform]
B2 --> B30[old-object-queue-size
Introduced in JDK 11
Default: 256
Function: OldObjectSample queue size]
B2 --> B31[dumppath
Introduced in JDK 17
Default: Current working directory
Function: Emergency dump path]
B2 --> B32[preserve-repository
Introduced in JDK 21
Default: false
Function: Whether to preserve Repository on JVM exit]
B2 --> B33[sampleprotection
Introduced in JDK 11
DEBUG mode only
Default: false/true
Function: Protection measures for stack traversal when sampling threads]
C --> C1[Single Recording Configuration
Specified via -XX:StartFlightRecording=
or jcmd JFR.start
or JDK API
or JMX]
C --> C2[Recording Level but Globally Affecting Configuration
Each recording can set, but actually affects globally]
C1 --> C11[settings
Introduced in JDK 11
Default: default.jfc
Function: Event configuration file]
C1 --> C12[name
Introduced in JDK 11
Default: null
Function: Recording name]
C1 --> C13[delay
Introduced in JDK 11
Default: 0s
Function: Delay start time]
C1 --> C14[duration
Introduced in JDK 11
Default: 0s
Function: Auto-stop time]
C1 --> C15[filename
Introduced in JDK 11
Default: null
Function: Filename when dumping]
C1 --> C16[maxage
Introduced in JDK 11
Default: 0s
Function: Maximum retention time]
C1 --> C17[maxsize
Introduced in JDK 11
Default: 0
Function: Maximum total size]
C1 --> C18[dumponexit
Introduced in JDK 11
Default: false
Function: Whether to dump on JVM exit]
C1 --> C19[path-to-gc-roots
Introduced in JDK 11
Default: false
Function: Whether to record GC roots]
C1 --> C20[report-on-exit
Introduced in JDK 25
Default: null
Function: Generate report view on JVM exit]
C2 --> C21[disk
Introduced in JDK 11
Default: true
Function: Whether to save to disk
Impact: Globally shared chunk files]
C2 --> C22[flush-interval
Introduced in JDK 17
Default: 1s
Function: Periodic flush interval
Impact: Uses minimum value of all recordings]
C11 --> C23[jfc files
default.jfc, profile.jfc]
C23 --> C24[enabled: Introduced in JDK 11
All event types
Controls whether events are recorded]
C23 --> C25[threshold: Introduced in JDK 11
Only valid for duration events
Specifies duration threshold]
C23 --> C26[period: Introduced in JDK 11
Only valid for periodic events
Specifies periodic event collection interval]
C23 --> C27[stackTrace: Introduced in JDK 11
All event types
Controls whether to collect stack traces]
C23 --> C28[throttle: Introduced in JDK 16
All event types
Controls maximum event collection rate]
C23 --> C29[filter: Introduced in JDK 25
Only valid for specific events
Specifies method filter]
C23 --> C30[cutoff: Introduced in JDK 11
Only valid for jdk.OldObjectSample
Limits maximum time to find GC root paths]
C23 --> C31[level: Introduced in JDK 22
Only valid for jdk.DeprecatedInvocation
Controls which deprecated method invocations are recorded]
%% Root node and main categories
style A fill:#2c3e50,stroke:#34495e,stroke-width:3px,color:#fff
style B fill:#3498db,stroke:#2980b9,stroke-width:2px,color:#fff
style C fill:#e67e22,stroke:#d35400,stroke-width:2px,color:#fff
%% Global configuration - Enable flag (special)
style B1 fill:#27ae60,stroke:#229954,stroke-width:2px,color:#fff
%% Global configuration - Container
style B2 fill:#5dade2,stroke:#3498db,stroke-width:2px,color:#fff
%% Global configuration - Buffer related (blue tones)
style B22 fill:#aed6f1,stroke:#5dade2,stroke-width:1.5px
style B23 fill:#aed6f1,stroke:#5dade2,stroke-width:1.5px
style B24 fill:#aed6f1,stroke:#5dade2,stroke-width:1.5px
style B25 fill:#aed6f1,stroke:#5dade2,stroke-width:1.5px
%% Global configuration - Storage related (green tones)
style B21 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
style B26 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
%% Global configuration - Sampling related (purple tones)
style B27 fill:#d2b4de,stroke:#bb8fce,stroke-width:1.5px
style B33 fill:#d2b4de,stroke:#bb8fce,stroke-width:1.5px
%% Global configuration - Other basic configurations (light blue tones)
style B28 fill:#d6eaf8,stroke:#85c1e9,stroke-width:1.5px
style B29 fill:#d6eaf8,stroke:#85c1e9,stroke-width:1.5px
style B30 fill:#d6eaf8,stroke:#85c1e9,stroke-width:1.5px
%% Global configuration - New version features (pink tones, JDK 17+)
style B31 fill:#f8c9d4,stroke:#ec7063,stroke-width:1.5px
style B32 fill:#f8c9d4,stroke:#ec7063,stroke-width:1.5px
%% Recording level configuration - Container
style C1 fill:#f39c12,stroke:#e67e22,stroke-width:2px,color:#fff
style C2 fill:#e74c3c,stroke:#c0392b,stroke-width:2px,color:#fff
%% Recording level configuration - Single recording configuration (orange tones)
style C11 fill:#fad7a0,stroke:#f39c12,stroke-width:1.5px
style C12 fill:#fad7a0,stroke:#f39c12,stroke-width:1.5px
style C13 fill:#fad7a0,stroke:#f39c12,stroke-width:1.5px
style C14 fill:#fad7a0,stroke:#f39c12,stroke-width:1.5px
style C15 fill:#fad7a0,stroke:#f39c12,stroke-width:1.5px
style C16 fill:#fad7a0,stroke:#f39c12,stroke-width:1.5px
style C17 fill:#fad7a0,stroke:#f39c12,stroke-width:1.5px
style C18 fill:#fad7a0,stroke:#f39c12,stroke-width:1.5px
style C19 fill:#fad7a0,stroke:#f39c12,stroke-width:1.5px
%% Recording level configuration - New version features (red tones, JDK 22+)
style C20 fill:#f1948a,stroke:#e74c3c,stroke-width:1.5px
%% Recording level configuration - Globally affecting configuration (dark orange tones)
style C21 fill:#f5b041,stroke:#e67e22,stroke-width:1.5px
style C22 fill:#f5b041,stroke:#e67e22,stroke-width:1.5px
%% jfc file configuration (green tones)
style C23 fill:#58d68d,stroke:#27ae60,stroke-width:2px,color:#fff
style C24 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
style C25 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
style C26 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
style C27 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
style C28 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
style C29 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
style C30 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
style C31 fill:#a9dfbf,stroke:#52be80,stroke-width:1.5px
1.3. JFR Configuration Details and Evolution#
- Global Configuration: Controls JFR feature enablement and global options
- Whether to enable JFR, if not enabled the module won’t even be loaded:
-XX:+FlightRecorder(Introduced in JDK 11, default value: false. This configuration is deprecated, and although the default value is false, if not set, the JVM still sets it to true, i.e., JFR is enabled by default. JFR functionality is only disabled when explicitly set to false) - Global Options: Specified via
-XX:FlightRecorderOptions=, or modified viajcmd <pid> JFR.configurecommand, or modified via JMXrepository: Introduced in JDK 11, default value: null (actually uses the directory specified by thejava.io.tmpdirsystem property, i.e., Java temporary file directory), function: Flight recorder disk repository location (Repository path):- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Specifies the disk directory where JFR stores chunk files. When disk recording is enabled, JFR creates subdirectories in this directory (format:
<timestamp>_<pid>) to store chunk files. Affects disk I/O performance and storage space management - Runtime dynamic modification: Can be modified at runtime via
jcmd <pid> JFR.configure repositorypath=<path>or JMX. If JFR is already initialized, modification will switch to the new repository path, old repository data will remain at the original location and will not be migrated. If the old repository directory is already incleanupDirectories(i.e., previously created vianewChunk()), JFR will continue to track it and automatically delete it on JVM shutdown (ifpreserve-repository=false). If there are running disk recordings, the current chunk will be marked as final and rotated, and new chunks will be written to the new repository path - Related JBS:
- JDK-8243452: Fixed issue where chunks could not be created in the repository when there were more than 200 recordings (Fix Version: 15) - https://bugs.openjdk.org/browse/JDK-8243452
globalbuffersize: Introduced in JDK 11, default value: 512k, function: Global buffer size:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Size of a single global buffer (range: 64K-2G). Together with
numglobalbuffers, determines total memory usage (memorysize = globalbuffersize * numglobalbuffers). Larger buffers can retain more event data at once, but also consume more memory. Generally recommended to adjust viamemorysizeuniformly - Runtime dynamic modification: Cannot be modified at runtime. Can only be set via
-XX:FlightRecorderOptionsbefore JFR initialization. If JFR is already initialized, modifying this option viajcmd JFR.configurewill be ignored (parameter will not be passed to Java layer) - Related JBS:
- JDK-8213015: Fixed inconsistency between
JFR.configureand-XX:FlightRecorderOptionssettings, unified value format and default values for options likeglobalbuffersize(Fix Version: 12) - https://bugs.openjdk.org/browse/JDK-8213015 - JDK-8203457: Fixed issue where no notification was sent to recorder thread to flush to disk when global buffer was full (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8203457
- JDK-8242088: Replaced mutex list with concurrent alternative, improved concurrent performance of buffer management (Fix Version: 15) - https://bugs.openjdk.org/browse/JDK-8242088
- JDK-8213015: Fixed inconsistency between
numglobalbuffers: Introduced in JDK 11, default value: 20, function: Number of global buffers:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Number of global buffers (minimum: 2). More buffers provide stronger concurrent write capability, but also consume more memory. Together with
globalbuffersize, determines total memory usage. Generally recommended to adjust viamemorysizeuniformly - Runtime dynamic modification: Cannot be modified at runtime. Can only be set via
-XX:FlightRecorderOptionsbefore JFR initialization. If JFR is already initialized, modifying this option viajcmd JFR.configurewill be ignored (parameter will not be passed to Java layer). - Related JBS:
- JDK-8213015: Fixed inconsistency between
JFR.configureand-XX:FlightRecorderOptionssettings, unified value format and default values for options likeglobalbuffercount(Fix Version: 12) - https://bugs.openjdk.org/browse/JDK-8213015 - JDK-8203457: Fixed issue where no notification was sent to recorder thread to flush to disk when global buffer was full (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8203457
- JDK-8242088: Replaced mutex list with concurrent alternative, improved concurrent performance of buffer management (Fix Version: 15) - https://bugs.openjdk.org/browse/JDK-8242088
- JDK-8213015: Fixed inconsistency between
memorysize: Introduced in JDK 11, default value: 10m, function: Size of memory to be used by Flight Recorder:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Total memory size of JFR global buffers (minimum: 1M), calculation formula:
memorysize = globalbuffersize * numglobalbuffers. Note:memorysizeonly includes global buffer memory, does not include thread-local buffer memory. Thread-local buffers are allocated independently via C heap, not limited bymemorysize. Actual total memory used by JFR =memorysize+ thread-local buffer memory (threadbuffersize * active thread count, may actually be less as buffers are dynamically allocated). If only this option is set, JFR will automatically calculate the optimal combination ofglobalbuffersizeandnumglobalbuffers - Runtime dynamic modification: Cannot be modified at runtime. Can only be set via
-XX:FlightRecorderOptionsbefore JFR initialization. If JFR is already initialized, modifying this option viajcmd JFR.configurewill be ignored (parameter will not be passed to Java layer) - Related JBS:
- JDK-8213015: Fixed inconsistency between
JFR.configureand-XX:FlightRecorderOptionssettings, unified value format and default values for options likememorysize(Fix Version: 12) - https://bugs.openjdk.org/browse/JDK-8213015 - JDK-8203457: Fixed issue where no notification was sent to recorder thread to flush to disk when global buffer was full (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8203457
- JDK-8242088: Replaced mutex list with concurrent alternative, improved concurrent performance of buffer management (Fix Version: 15) - https://bugs.openjdk.org/browse/JDK-8242088
- JDK-8213015: Fixed inconsistency between
threadbuffersize: Introduced in JDK 11, default value: 8k (or OS page size, whichever is larger), function: Thread buffer size:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Local buffer size per thread (range: 4K-2G, must be ≤
globalbuffersize). Important:threadbuffersizeis not affected bymemorysize, thread-local buffers are allocated independently via C heap (JfrCHeapObj), which is off-heap memory. Thread buffers are used to temporarily store event data, and when full, flush to global buffers. Larger buffers allow threads to cache more events locally, reducing global buffer contention, but also increase memory usage per thread. Note: The thread-local buffer pool’s free_list caches at most 8 buffers (thread_local_cache_count = 8, hardcoded in JDK source code and cannot be modified), which is a cache limit, not a thread count limit. When the number of active threads exceeds 8, additional threads will directly allocate new buffers; when buffers are released, if free_list is full (8 buffers), memory is directly released rather than cached. Actual total memory used by JFR =memorysize+ thread-local buffer memory (threadbuffersize * active thread count, may actually be less as buffers are dynamically allocated) - Runtime dynamic modification: Cannot be modified at runtime. Can only be set via
-XX:FlightRecorderOptionsbefore JFR initialization. If JFR is already initialized, modifying this option viajcmd JFR.configurewill be ignored (parameter will not be passed to Java layer) - Related JBS:
- JDK-8213015: Fixed inconsistency between
JFR.configureand-XX:FlightRecorderOptionssettings, unified value format and default values for options likethread_buffer_size(Fix Version: 12) - https://bugs.openjdk.org/browse/JDK-8213015
- JDK-8213015: Fixed inconsistency between
maxchunksize: Introduced in JDK 11, default value: 12m, function: Maximum size of a single repository disk chunk:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Maximum size of a single chunk file (minimum: 1M). When a chunk reaches this size, rotation is triggered, creating a new chunk file. Larger values result in fewer chunk files, but larger individual files; smaller values result in more chunk files, but facilitate segmented analysis. Affects disk write frequency and file management
- Runtime dynamic modification: Cannot be modified at runtime. Can only be set via
-XX:FlightRecorderOptionsbefore JFR initialization. If JFR is already initialized, modifying this option viajcmd JFR.configurewill be ignored (parameter will not be passed to Java layer) - Related JBS:
- JDK-8213015: Fixed inconsistency between
JFR.configureand-XX:FlightRecorderOptionssettings, unified value format and default values for options likemaxchunksize(Fix Version: 12) - https://bugs.openjdk.org/browse/JDK-8213015 - JDK-8243452: Fixed issue where chunks could not be created in the repository when there were more than 200 recordings (Fix Version: 15) - https://bugs.openjdk.org/browse/JDK-8243452
- JDK-8240783: Fixed issue where TestClose test could not complete chunks (Fix Version: 15) - https://bugs.openjdk.org/browse/JDK-8240783
- JDK-8236487: Fixed JFR Recorder Thread crash issue, crash occurred when chunk writer was invalid (Fix Version: 14) - https://bugs.openjdk.org/browse/JDK-8236487
- JDK-8213015: Fixed inconsistency between
samplethreads: Introduced in JDK 11, deprecated in JDK 19 (but still available), default value: true, function: Thread sampling enable / disable (only sampling when event enabled and sampling enabled):- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Deprecation: JDK-8259774: Deprecate -XX:FlightRecorderOptions:samplethreads (Fix Version: 19) - https://bugs.openjdk.org/browse/JDK-8259774
- Impact: Controls whether thread sampling is enabled. When false, even if events requiring sampling (such as ExecutionSample) are enabled, thread sampling will not occur. After JDK 19, it is recommended to use
-XX:StartFlightRecording:method-profilinginstead - Related JBS:
- JDK-8203635: Fixed issue where JFR sampler thread did not record stack information, which is important for NMT (Native Memory Tracking) (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8203635
- JDK-8215727: Restored old behavior of JFR thread sampler loop, ensuring sampler can collect sufficient samples (Fix Version: 13) - https://bugs.openjdk.org/browse/JDK-8215727
- JDK-8240819: Allocated a name for JfrThreadSampler thread for easier debugging (Fix Version: 15) - https://bugs.openjdk.org/browse/JDK-8240819
- JDK-8288663: Fixed issue where only partial disabled state was submitted when JfrThreadSampler was disabled (Fix Version: 20) - https://bugs.openjdk.org/browse/JDK-8288663
- JDK-8367953: Fixed issue where JFR sampler thread did not appear in thread dumps (Fix Version: 26) - https://bugs.openjdk.org/browse/JDK-8367953
stackdepth: Introduced in JDK 11, default value: 64, function: Stack depth for stacktraces (minimum 1, maximum 2048):- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Maximum depth of stack traces (range: 1-2048). Deeper traces provide more complete stack information, but also consume more memory and CPU overhead (stack traversal cost). Too shallow may lose critical call information. Need to balance information completeness and performance overhead
- Runtime dynamic modification: Cannot be modified at runtime. Can only be set via
-XX:FlightRecorderOptionsbefore JFR initialization. If JFR is already initialized, modifying this option viajcmd JFR.configurewill be ignored (parameter will not be passed to Java layer). JDK 17 fixed enforcement of this contract (JDK-8278419) - Related JBS:
- JDK-8278419: Fixed issue where contracts for certain options in jcmd JFR.configure command were not enforced,
stackdepthcannot be changed after JFR initialization (Fix Version: 17) - https://bugs.openjdk.org/browse/JDK-8278419 - JDK-8249713: Fixed issue where stack traces for java.base events were incomplete, skip level for stack traces was set too large (Fix Version: 15) - https://bugs.openjdk.org/browse/JDK-8249713
- JDK-8278419: Fixed issue where contracts for certain options in jcmd JFR.configure command were not enforced,
retransform: Introduced in JDK 11, default value: true, function: If event classes should be instrumented using JVMTI (by default true):- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact:
- retransform=true (default): Uses JVMTI RetransformClasses functionality, can dynamically instrument event classes after class loading, supports dynamic event enablement and Method Tracing functionality
- retransform=false: Uses “eager instrumentation”, only instruments during initial class loading; event classes that are already loaded but not instrumented cannot be instrumented again, new filters for method tracing will be ignored, may affect ability to dynamically enable events
- Runtime dynamic modification: Cannot be modified at runtime. Can only be set via
-XX:FlightRecorderOptionsbefore JFR initialization. This option is not in the support list ofjcmd JFR.configurecommand
old-object-queue-size: Introduced in JDK 11, default value: 256, function: Maximum number of old objects to track:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Maximum number of old objects tracked by OldObjectSample events. Larger values allow tracking more potential memory leak objects, but also consume more memory (each object needs to store reference chain information). Setting to 0 disables OldObjectSample events, saving memory
- Runtime dynamic modification: Cannot be modified at runtime. Can only be set via
-XX:FlightRecorderOptionsbefore JFR initialization. This option is not in the support list ofjcmd JFR.configurecommand - Related JBS:
- JDK-8212232: Fixed incorrect cutoff configuration metadata for OldObjectSample events, metadata showed as BooleanFlag type but should be Timespan type (Fix Version: 12) - https://bugs.openjdk.org/browse/JDK-8212232
- JDK-8214542: Fixed issue where Old Object Sample events ran slowly on deep heaps in debug builds (Fix Version: 13) - https://bugs.openjdk.org/browse/JDK-8214542
- JDK-8313394: Fixed issue where Array Elements field description was incorrect in OldObjectSample events (Fix Version: 21) - https://bugs.openjdk.org/browse/JDK-8313394
- JDK-8330215: Optimized OldObjectSamples working set, reduced memory usage (Fix Version: 22) - https://bugs.openjdk.org/browse/JDK-8330215
dumppath: Introduced in JDK 17, default value: null (actually uses current working directory, i.e., current directory when JVM starts), function: Path to emergency dump:- Introduction: JDK-8271949: dumppath in -XX:FlightRecorderOptions does not affect (Fix Version: 17) - https://bugs.openjdk.org/browse/JDK-8271949
- Impact: Specifies the directory where emergency dump files are saved. When JVM encounters abnormal shutdown (crash, OOM, stack overflow, etc.), JFR will merge all data in the repository and write to an emergency dump file (filename format:
hs_err_pid<pid>.jfr,hs_oom_pid<pid>.jfrorhs_soe_pid<pid>.jfr). If the specified directory cannot be written to, it will automatically fall back to the current working directory. This is very important for saving JFR data for problem diagnosis in abnormal situations - Emergency Dump Details:
- Function: When JVM crashes or abnormally shuts down, automatically merges all JFR chunk files in the repository in chronological order and writes to an emergency dump file, ensuring JFR data can be saved for problem diagnosis even in abnormal situations
- Trigger Conditions:
- On VM Error: Triggered via
JfrEmergencyDump::on_vm_error()(called inJfrRepository::on_vm_error()), triggered when JVM encounters serious errors (such as crashes, assertion failures, etc.) - On VM Shutdown: Triggered via
JfrEmergencyDump::on_vm_shutdown()(called inJfr::on_vm_shutdown(), called inbefore_exit()injava.cpp), also triggered when JVM shuts down normally - Note: If WatcherThread crashes, emergency dump will not be generated (because WatcherThread is a safety net, used to timeout and exit when emergency dump deadlocks)
- On VM Error: Triggered via
- Dump Content:
- Repository Data: Merges all
.jfrchunk files in the repository directory in chronological order (ISO8601 timestamp) and writes to emergency dump file - Event Data: When
on_vm_shutdown()is called, the following events are also triggered and written to the dump file:EventDumpReason: Records dump reason (reasonfield is “Out of Memory” or “Crash”)EventShutdown: Ifemit_event_shutdown=true, records VM shutdown event- Old Object Samples: If
emit_old_object_samples=true, emits old object samples (for OOM analysis)
- Repository Data: Merges all
- File Naming Rules:
- General Error:
hs_err_pid<pid>.jfr(default case, including crashes, assertion failures, etc.) - Out of Memory (OOM):
hs_oom_pid<pid>.jfr(whenJfrJavaSupport::cause()returnsOUT_OF_MEMORY) - Stack Overflow (SOE):
hs_soe_pid<pid>.jfr(whenJfrJavaSupport::cause()returnsSTACK_OVERFLOW)
- General Error:
- Dump Path:
- If
dumppathis set, emergency dump file will be created in that directory - If
dumppathis not set or setting fails, falls back to current working directory - If specified directory cannot be written to, logs warning and attempts to create in current directory
- If
- Implementation Details:
- Uses 1MB buffer blocks for file copying to avoid out of memory
- Releases all held locks (Threads_lock, Module_lock, Heap_lock, etc.) during dump to avoid deadlocks
- Uses reentrancy protection mechanism (
guard_reentrancy()), ensuring only one thread executes emergency dump at a time - If deadlock occurs during emergency dump, WatcherThread acts as safety net and forces JVM exit after timeout
- Runtime dynamic modification: Can be modified at runtime via
jcmd <pid> JFR.configure dumppath=<path>or JMX - Related JBS:
- JDK-8206254: Fixed issue where JFR emergency dump could not complete during safepoint (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8206254
- JDK-8233706: Fixed issue where emergency dump executed before error report, now emergency dump executes after error report (such as NMT report, CI Replay) (Fix Version: 15) - https://bugs.openjdk.org/browse/JDK-8233706
- JDK-8235390: Fixed crash issue in
JfrEmergencyDump::on_vm_shutdown, safely completes dump during VM shutdown (Fix Version: 14) - https://bugs.openjdk.org/browse/JDK-8235390 - JDK-8249878: Fixed secondary crash issue in emergency dump, improved handling of non-JavaThread (such as VMThread) (Fix Version: 16) - https://bugs.openjdk.org/browse/JDK-8249878
- JDK-8282947: Fixed issue where dump on shutdown caused livelock under certain conditions (Fix Version: 20) - https://bugs.openjdk.org/browse/JDK-8282947
preserve-repository: Introduced in JDK 21, default value: false, function: Preserve disk repository after JVM exit:- Introduction: JDK-8303229: JFR: Preserve disk repository after exit (Fix Version: 21) - https://bugs.openjdk.org/browse/JDK-8303229
- Impact: Controls whether to preserve chunk files in repository directory when JVM exits. When true, repository files are not cleaned up after normal JVM exit, facilitating subsequent analysis; when false, repository is automatically cleaned up on JVM exit to avoid disk space usage
- Runtime dynamic modification: Can be modified at runtime via
jcmd <pid> JFR.configure preserve-repository=<true|false>or JMX
sampleprotection: Introduced in JDK 11 (DEBUG mode only), default value: false (DEBUG mode)/true (non-DEBUG mode), function: Safeguard for stackwalking while sampling threads (false by default):- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Only available in DEBUG builds, used to protect stack traversal operations when sampling threads. Default is false in DEBUG mode, default is true in non-DEBUG mode. Mainly used in development and debugging scenarios, usually not available in production environments
- Whether to enable JFR, if not enabled the module won’t even be loaded:
- Recording Level Configuration: Controls event configuration and storage options for individual recordings. A JVM process can have multiple recordings running simultaneously, each recording configuration can be set independently without affecting each other
- Single Recording Configuration: Specified via
-XX:StartFlightRecording=, or modified viajcmd <pid> JFR.startcommand, or specified via JDK API, or specified via JMXsettings: Introduced in JDK 11, default value: default.jfc, function: Settings file(s) that identifies which events to record:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Specifies JFR event configuration file (.jfc file), used to determine which events to record and their configuration. Multiple configuration files can be specified by repeating the settings parameter. If file is not in JAVA_HOME/lib/jfr directory, full path must be included. Can use “none” to start a recording without predefined configuration. Default uses default.jfc, providing low-overhead predefined event set, suitable for continuous use in production environments. Each recording can be set independently without affecting each other
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()or JMX while recording is running. Changes take effect immediately. If recording is running (RUNNING state),recorder.updateSettings(true)is called to update event configuration - Related JBS:
- JDK-8216064: Fixed issue where
-XX:StartFlightRecording:settings=option did not work properly, improved error handling and supports “none” value (Fix Version: 13) - https://bugs.openjdk.org/browse/JDK-8216064
- JDK-8216064: Fixed issue where
- Individual Event Configuration:
enabled- Introduced in JDK 11- Function: Controls whether events are recorded
- Applicable Events: All event types
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()or JMX while recording is running, changes take effect immediately
threshold- Introduced in JDK 11- Function: Specifies duration threshold, events below this threshold are not recorded
- Applicable Events: Only valid for events with duration
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()or JMX while recording is running, changes take effect immediately - Example:
jdk.ThreadSleep#threshold=20 msmeans only record thread sleep events with sleep time exceeding 20ms
period- Introduced in JDK 11- Function: Specifies collection interval for periodic events
- Default value:
"everyChunk"(collect on each chunk rotation) - Applicable Events: Only valid for periodic events
- Configuration values:
- Chunk-related values:
"everyChunk"(default): Collect events on each chunk rotation, including start and end. Number of events depends on chunk rotation count. If no rotation during recording period, at least collect once. Suitable for statistical events (such asjdk.ClassLoadingStatistics,jdk.ThreadAllocationStatistics)"beginChunk": Only collect when chunk starts rotation. Collect once at the start of each new chunk"endChunk": Only collect when chunk ends rotation. Collect once at the end of each chunk
- Time interval values (fixed frequency):
- Format:
<number> <unit> - Supported units:
ns(nanoseconds),us(microseconds),ms(milliseconds),s(seconds),m(minutes),h(hours),d(days) - Examples:
"20 ms"(every 20 milliseconds),"1 s"(every second),"5 m"(every 5 minutes),"1 h"(every hour) - Minimum interval: Actual minimum interval is 1 millisecond, even if smaller value is set, it will be adjusted to 1 millisecond
- If set to
"0 ns"orLong.MAX_VALUE, will be treated as disabled periodic collection
- Format:
- Chunk-related values:
- Value merging rules (when multiple recordings run simultaneously):
- If time interval value is specified, use the minimum time interval
- If only chunk-related values are specified, follow these rules:
- Both
beginChunkandendChunkspecified → equivalent toeveryChunk - Only
beginChunkspecified → returnbeginChunk - Only
endChunkspecified → returnendChunk - Default return
everyChunk
- Both
- Examples:
jdk.CPULoad#period=1 smeans collect CPU load event every secondjdk.ClassLoadingStatistics#period=everyChunkmeans collect class loading statistics on each chunk rotation
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()or JMX while recording is running, changes take effect immediately - Notes:
periodis only valid for periodic events: only events annotated with@Periodsupport this configuration- Chunk rotation frequency: Collection frequency of
everyChunk,beginChunk,endChunkdepends on chunk rotation frequency, which is controlled by global configurations likemaxchunksize - Performance impact: Smaller time intervals mean higher collection frequency and greater performance overhead
stackTrace- Introduced in JDK 11- Function: Controls whether to collect stack traces when events are committed
- Applicable Events: All event types (but collecting stack traces for some events doesn’t make much sense, such as timed events)
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()or JMX while recording is running, changes take effect immediately
throttle- Introduced in JDK 16- Function: Controls maximum collection rate for events (per second/per minute, etc.)
- Applicable Events: All event types
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()or JMX while recording is running, changes take effect immediately - Example:
jdk.ObjectAllocationSample#throttle=100/smeans collect at most 100 object allocation sample events per second
filter- Introduced in JDK 25- Introduction: JDK-8352738: Implement JEP 520: JFR Method Timing and Tracing (Fix Version: 25) - https://bugs.openjdk.org/browse/JDK-8352738
- Function: Specifies method filter for filtering methods to record
- Applicable Events: Only valid for specific events, mainly:
jdk.MethodTrace(method tracing events)jdk.MethodTiming(method timing events)
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()or JMX while recording is running, changes take effect immediately - Syntax: Supports filtering rules for class names, method names, annotations, etc.
cutoff- Introduced in JDK 11- Function: Limits maximum time to find GC root paths, used to control time overhead of finding object reference chains in
jdk.OldObjectSampleevents - Default value:
"infinity"(no time limit) - Applicable Events: Only valid for
jdk.OldObjectSampleevents - Configuration values:
- Time interval format:
"1 h","0 ns","infinity", etc. "0 ns": Do not find GC root paths, only record object itself (no reference chain information), minimum performance overhead"infinity": No time limit, complete GC root path search- Other time values: Limit maximum time to find paths, exceeding time limit may not obtain complete reference chain
- Time interval format:
- Relationship with
path-to-gc-roots:- When
path-to-gc-roots=true,cutoffis automatically set to"infinity" - When
path-to-gc-roots=false,cutoffis automatically set to"0 ns"
- When
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()or JMX while recording is running, changes take effect immediately - Example:
jdk.OldObjectSample#cutoff=1 hmeans maximum time to find GC root paths is 1 hour - Note:
cutoffdoes not directly filter object age, but limits time to find reference chains. Object age is recorded through event’sobjectAgefield
- Function: Limits maximum time to find GC root paths, used to control time overhead of finding object reference chains in
level- Introduced in JDK 22- Introduction: JDK-8211238: @Deprecated JFR event (Fix Version: 22) - https://bugs.openjdk.org/browse/JDK-8211238
- Function: Controls which deprecated method invocations are recorded in
jdk.DeprecatedInvocationevents - Default value:
"forRemoval" - Applicable Events: Only valid for
jdk.DeprecatedInvocationevents - Configuration values:
"forRemoval"(level 0, default): Only record method invocations marked as@Deprecated(forRemoval=true), lower overhead"all"(level 1): Record all@Deprecatedmethod invocations (includingforRemoval=false), more complete information but higher overhead
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()or JMX while recording is running, changes take effect immediately - Example:
jdk.DeprecatedInvocation#level=allmeans record all deprecated method invocations - Note: GC Phase related events (such as
jdk.GCPhasePauseLevel1,jdk.GCPhasePauseLevel2, etc.) are distinguished by different event names, not controlled bylevelconfiguration
name: Introduced in JDK 11, default value: null (system generated), function: Name that can be used to identify recording:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Specifies identifier name for recording, used to distinguish different recordings among multiple recordings. If name is not specified, system automatically generates a name. Recording name cannot be pure numbers to avoid confusion with recording ID. Name is used to manage recordings via jcmd commands (such as JFR.dump, JFR.stop, etc.). Each recording can be set independently without affecting each other
- Runtime dynamic modification: Can be modified at runtime via
Recording.setName()or JMX while recording is running, can be modified as long as recording is not in CLOSED state
delay: Introduced in JDK 11, default value: 0s, function: Length of time to wait before starting to record:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Specifies how long to wait after JVM startup before starting recording. Time format: integer followed by ’s’ (seconds), ’m’ (minutes), ‘h’ (hours) or ’d’ (days). Minimum value is 1 second. Delayed start can be used to skip application startup phase, only record data after application is stable, reducing recording file size and startup overhead. Each recording can be set independently without affecting each other
- Runtime dynamic modification: Cannot be modified while recording is running. Can only be set via
Recording.scheduleStart()when recording is created (NEW state), cannot be modified once recording enters DELAYED or RUNNING state
duration: Introduced in JDK 11, default value: 0s (unlimited), function: Length of time to record:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Specifies recording duration, time format: integer followed by ’s’ (seconds), ’m’ (minutes), ‘h’ (hours) or ’d’ (days). 0s means unlimited, recording will run until manually stopped. Minimum value is 1 second. When specified time is reached, recording automatically stops. If filename is also specified, recording will automatically dump to file after stopping. Each recording can be set independently without affecting each other
- Runtime dynamic modification: Can be modified at runtime via
Recording.setDuration()or JMX while recording is running, can be modified as long as recording is not in STOPPED or CLOSED state. After modification, stop time is recalculated and timer is updated
filename: Introduced in JDK 11, default value: null (system generated), function: Name of the file to which the flight recording data is written when the recording is stopped:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Specifies file path and name to dump when recording stops. If not specified, system automatically generates filename based on PID and current date. If directory is specified, filename will be generated in that directory. Filename can use %p (PID) and %t (timestamp, format: yyyy_MM_dd_HH_mm_ss) placeholders. If filename is specified, dumponexit=true by default. Each recording can be set independently without affecting each other
- Runtime dynamic modification: Can be modified at runtime via
Recording.setDestination()or JMX while recording is running, can be modified as long as recording is not in STOPPED or CLOSED state - Related JBS:
- JDK-8323425: Fixed issue where auto-generated filename did not work in time-limited recordings (Fix Version: 23) - https://bugs.openjdk.org/browse/JDK-8323425
maxage: Introduced in JDK 11, default value: 0s (unlimited), function: Maximum time to keep the recorded data on disk:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Specifies maximum retention time for recorded data on disk, time format: integer followed by ’s’ (seconds), ’m’ (minutes), ‘h’ (hours) or ’d’ (days). 0s means unlimited. This parameter is only effective when disk=true. Old chunk files exceeding specified time are automatically deleted, used to control disk space usage. Can be used together with maxsize to limit both time and size. Note: Although chunk files from different recordings are stored in the same repository directory, each recording tracks its own chunks through its own chunks list, each recording independently applies maxage limit, only deletes chunks it no longer needs (managed through reference counting mechanism)
- Runtime dynamic modification: Can be modified at runtime via
Recording.setMaxAge()or JMX while recording is running, can be modified as long as recording is not in CLOSED state. After modification,trimToAge()is immediately triggered to clean up old chunks exceeding retention time - Related JBS:
- JDK-8203929: Added
maxageparameter toJFR.dumpcommand, allowing to limit amount of dumped data (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8203929 - JDK-8294242: Fixed issue where
jfr printcommand handled unlimited duration, displays more friendly when maxAge is unlimited (Fix Version: 20) - https://bugs.openjdk.org/browse/JDK-8294242
- JDK-8203929: Added
maxsize: Introduced in JDK 11, default value: 0 (unlimited), function: Maximum size of the data to keep on disk:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Specifies maximum total size of recorded data on disk, supports ‘k’/‘K’ (KB), ’m’/‘M’ (MB) or ‘g’/‘G’ (GB) suffix. 0 means unlimited. This parameter is only effective when disk=true. Value cannot be less than maxchunksize in global configuration. When specified size is reached, oldest chunk files are automatically deleted, used to control disk space usage. Can be used together with maxage to limit both size and time. Note: Although chunk files from different recordings are stored in the same repository directory, each recording tracks its own chunks through its own chunks list, each recording independently applies maxsize limit, only deletes chunks it no longer needs (managed through reference counting mechanism)
- Runtime dynamic modification: Can be modified at runtime via
Recording.setMaxSize()or JMX while recording is running, can be modified as long as recording is not in CLOSED state. After modification,trimToSize()is immediately triggered to clean up old chunks exceeding size limit - Related JBS:
- JDK-8203929: Added
maxsizeparameter toJFR.dumpcommand, allowing to limit amount of dumped data (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8203929
- JDK-8203929: Added
dumponexit: Introduced in JDK 11, default value: false, function: Flag for writing the recording to disk when the JVM shuts down:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Controls whether to dump recording to file when JVM exits. When true, recording is automatically dumped when JVM exits normally or abnormally (such as OOM). If filename is not specified, system filename will be generated in process startup directory (format:
id-<recording-id>-<timestamp>.jfr). If filename is specified, dumponexit=true by default. This is very important for saving JFR data for problem diagnosis in abnormal situations. Each recording can be set independently without affecting each other - Runtime dynamic modification: Can be modified at runtime via
Recording.setDumpOnExit()or JMX while recording is running, no state restrictions, can be modified at any time - Related JBS:
- JDK-8198337: Fixed issue where dump file size was 0 when starting memory recording with
-XX:StartFlightRecording=dumponexit=true,disk=false(Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8198337 - JDK-8282947: Fixed issue where dump on shutdown caused livelock under certain conditions (Fix Version: 20) - https://bugs.openjdk.org/browse/JDK-8282947
- JDK-8198337: Fixed issue where dump file size was 0 when starting memory recording with
path-to-gc-roots: Introduced in JDK 11, default value: false, function: Flag for saving the path to garbage collection (GC) roots at the end of a recording:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Controls whether to save path information to GC roots at the end of recording. When true, GC root path information for objects in OldObjectSample events is collected at the end of recording, which is very useful for finding memory leaks, but collection process takes a long time. If settings parameter is set to ‘profile’, collected information also includes allocation stack traces of potential leak objects. Recommended to enable only when memory leak is suspected. Each recording can be set independently without affecting each other
- Runtime dynamic modification: Can be modified at runtime via
Recording.setSettings()to modifyjdk.OldObjectSample#path-to-gc-rootssetting or JMX while recording is running, changes take effect immediately. Note: Modifyingpath-to-gc-rootsautomatically synchronizes modification ofjdk.OldObjectSample#cutoffsetting (true → infinity, false → 0 ns)
report-on-exit: Introduced in JDK 25, default value: null, function: Generate report view(s) when the JVM shuts down:- Introduction: JDK-8351266: JFR: -XX:StartFlightRecording:report-on-exit (Fix Version: 25) - https://bugs.openjdk.org/browse/JDK-8351266
- Impact: Specifies report view identifiers to generate when JVM exits. Can repeat to specify multiple views, for example
report-on-exit=jvm-information,report-on-exit=system-properties. Report views are used to automatically generate analysis reports in specific format on exit, facilitating quick viewing of key information. Supported views include jvm-information, system-properties, etc. Each recording can be set independently without affecting each other - Runtime dynamic modification: Cannot be modified while recording is running. Can only be set when recording is created via
-XX:StartFlightRecordingorjcmd JFR.start - Related JBS:
- JDK-8359248: Improved help text for
-XX:StartFlightRecording:report-on-exitoption, explaining that the value can be repeated (Fix Version: 26) - https://bugs.openjdk.org/browse/JDK-8359248
- JDK-8359248: Improved help text for
- Recording Level but Globally Affecting Configuration: Each recording can be set independently, but actual impact is global, multiple recordings share underlying resources
disk: Introduced in JDK 11, default value: true, function: Flag for also writing the data to disk while recording:- Introduction: JDK-8199712: Flight Recorder (Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8199712
- Impact: Controls whether recording is written to disk. When true, data is continuously written to disk repository, supports maxage and maxsize limits; when false, data is only stored in memory, suitable for short-term recordings or memory-constrained scenarios. Memory recordings are only written to file on JVM exit or manual dump. Important: Although each recording can independently set disk parameter, actual impact is global. If any running recording is set to disk=true, system creates global chunk files and writes to disk repository. All running recordings share the same physical chunk file (
PlatformRecorder.currentChunk), when a chunk completes, it is allocated to all running recordings (viafinishChunk()method). This means even if a recording is set to disk=false, if other recordings are set to disk=true, that recording will also get data from shared chunks. Affects disk I/O performance and storage space management - Runtime dynamic modification: Cannot be modified while recording is running. Can only be set when recording is created (NEW or DELAYED state) via
Recording.setToDisk(), cannot be modified once recording enters RUNNING state - Related JBS:
- JDK-8198337: Fixed issue where dump file size was 0 when starting memory recording with
-XX:StartFlightRecording=dumponexit=true,disk=false(Fix Version: 11) - https://bugs.openjdk.org/browse/JDK-8198337 - JDK-8304844: Fixed issue where ActiveRecording event was missing disk parameter (Fix Version: 22) - https://bugs.openjdk.org/browse/JDK-8304844
- JDK-8198337: Fixed issue where dump file size was 0 when starting memory recording with
flush-interval: Introduced in JDK 17, default value: 1s, function: Minimum time before flushing buffers:- Introduction: JDK-8265036: JFR: Add flush-interval option (Fix Version: 17) - https://bugs.openjdk.org/browse/JDK-8265036
- Impact: Specifies minimum time interval before flushing buffer data to disk, time format: integer followed by ’s’ (seconds), ’m’ (minutes), ‘h’ (hours) or ’d’ (days). 0 means only flush when recording ends. This parameter is only effective when disk=true. Smaller intervals can reduce data loss risk, but increase disk I/O overhead; larger intervals can reduce I/O overhead, but may lose more data on abnormal exit. Need to balance data safety and performance overhead. Important: Although each recording can independently set flush-interval parameter, actual impact is global. System uses minimum flush-interval value of all running recordings as global flush interval (calculated via
Math.min()). This is because all recordings share the same physical chunk file, can only have one global flush interval.FlushTaskis a global singleton, all recordings share the same flush task. When starting or stopping recordings, system recalculates minimum flush-interval of all running recordings and updates global flush interval - Runtime dynamic modification: Can be modified at runtime via
Recording.setFlushInterval()or JMX while recording is running, can be modified as long as recording is not in CLOSED state. After modification, system recalculates minimum flush-interval of all running recordings and updates global flush interval
- Single Recording Configuration: Specified via
1.4. Event Type Classification and Configuration Applicability#
1.4.1. Classification by Collection Method#
Synchronous Events
- Characteristics: Committed directly in business threads, such as
jdk.ThreadStart,jdk.ThreadEnd - Applicable configurations:
enabled,stackTrace,threshold(if has duration),throttle,filter(if method-related events)
- Characteristics: Committed directly in business threads, such as
Asynchronous Events
- Characteristics: Collected in background threads, such as sampling events
- Applicable configurations:
enabled,stackTrace,throttle
Periodic Events
- Characteristics: Collected at fixed intervals, such as
jdk.CPULoad,jdk.ClassLoadingStatistics - Applicable configurations:
enabled,period,stackTrace(but doesn’t make sense, because we don’t care about stack traces for periodic triggers),throttle(also doesn’t make sense, period already controls frequency) - Special note:
periodconfiguration is only valid for this type of event
- Characteristics: Collected at fixed intervals, such as
Requestable Events
- Characteristics: Triggered on demand, such as chunk start/end events
- Applicable configurations:
enabled,stackTrace
1.4.2. Classification by Event Characteristics#
Duration Events
- Characteristics: Have clear start and end times, such as
jdk.ThreadSleep,jdk.GCPhase - Applicable configurations:
enabled,threshold,stackTrace,throttle - Special note:
thresholdconfiguration is only valid for this type of event
- Characteristics: Have clear start and end times, such as
Instant Events
- Characteristics: No duration, such as
jdk.ThreadStart,jdk.ClassLoad, also periodic events and requestable events mentioned above belong to instant events here - Applicable configurations:
enabled,stackTrace,throttle - Special note:
thresholdconfiguration is not supported
- Characteristics: No duration, such as
Sampling Events
- Characteristics: Collected through sampling mechanism, such as
jdk.ExecutionSample,jdk.ObjectAllocationSample - Applicable configurations:
enabled,stackTrace,throttle - Special note: Usually use
throttleto control sampling frequency
- Characteristics: Collected through sampling mechanism, such as
1.4.3. Classification by Implementation Level#
JDK Java-level Events
- Characteristics: Event classes are defined in JDK’s Java code, inherit from
AbstractJDKEventorjdk.jfr.Event - Implementation location:
- Event class definition: In
src/jdk.jfr/share/classes/jdk/jfr/events/package - Event triggering: Implemented in
jdk.jfr.internal.JDKEventsclass, or directly callEvent.commit()in Java code
- Event class definition: In
- Typical events:
jdk.ActiveRecording: Records JFR recording configuration informationjdk.ActiveSetting: Records event configuration settingsjdk.ThreadStart,jdk.ThreadEnd: Thread lifecycle eventsjdk.ClassLoad,jdk.ClassUnload: Class loading/unloading eventsjdk.FileRead,jdk.FileWrite: File I/O events (via JVMTI instrumentation)jdk.SocketRead,jdk.SocketWrite: Network I/O events (via JVMTI instrumentation)
- Implementation mechanism:
- Directly call
Event.commit()at key positions in Java code to commit events - For I/O related events, bytecode instrumentation is performed via JVMTI at class loading time or runtime, event commit is called in instrumented code
- Event classes are instrumented via JVMTI RetransformClasses or Eager Instrumentation mechanism
- Directly call
- Advantages: Simple implementation, easy to maintain and extend; can access rich information at Java layer
- Applicable scenarios: Events in JDK libraries, application custom events
- Characteristics: Event classes are defined in JDK’s Java code, inherit from
JVM C++-level Events
- Characteristics: Events are defined in JVM’s C++ code, JFR API is directly called at key points inside JVM
- Implementation location:
- Event metadata definition:
src/hotspot/share/jfr/metadata/metadata.xml - Event implementation: In JVM’s C++ code (under
src/hotspot/share/jfr/directory)
- Event metadata definition:
- Subcategories:
- Periodic Events:
- Implementation location:
src/hotspot/share/jfr/periodic/jfrPeriodic.cpp - Implementation method: Defined via
TRACE_REQUEST_FUNCmacro, triggered in periodic tasks - Typical events:
jdk.JVMInformation: JVM informationjdk.CPULoad: CPU loadjdk.ClassLoadingStatistics: Class loading statisticsjdk.ThreadAllocationStatistics: Thread allocation statisticsjdk.GCConfiguration,jdk.GCHeapConfiguration: GC configuration information
- Implementation location:
- JVM Internal Hook Events:
- Implementation method: Directly call JFR API at key execution paths in JVM
- Typical events:
jdk.GCPhase: GC phase events (triggered in GC code)jdk.GCHeapSummary: GC heap summary (triggered in GC code)jdk.ObjectAllocationInNewTLAB,jdk.ObjectAllocationOutsideTLAB: Object allocation events (triggered in object allocation paths)jdk.MonitorWait,jdk.MonitorWaited: Lock wait events (triggered in synchronization code)jdk.ThreadSleep,jdk.ThreadPark: Thread sleep/suspend events (triggered in thread code)
- Periodic Events:
- Advantages: Can access JVM internal state, low performance overhead (direct call, no JNI overhead)
- Applicable scenarios: JVM internal events, GC events, thread events, object allocation events, etc.
JVMTI Instrumentation-based Events
- Characteristics: Bytecode instrumentation is performed via JVMTI at class loading time or runtime, events are triggered in instrumented code
- Implementation location:
- Instrumentation logic:
src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp - JVMTI Agent:
src/hotspot/share/jfr/instrumentation/jfrJvmtiAgent.cpp - Java layer callback:
src/jdk.jfr/share/classes/jdk/jfr/internal/JVMUpcalls.java
- Instrumentation logic:
- Implementation mechanism:
- Eager Instrumentation: Instrument at initial class loading (when
retransform=false) - Retransform Instrumentation: Instrument after class loading via JVMTI RetransformClasses (when
retransform=true, default) - Instrumentation content: Insert code that calls JFR buffer in
Event.commit()method
- Eager Instrumentation: Instrument at initial class loading (when
- Typical events:
jdk.FileRead,jdk.FileWrite: File I/O events (instrumentjava.io.FileInputStream,FileOutputStream, etc.)jdk.SocketRead,jdk.SocketWrite: Network I/O events (instrumentjava.net.Socket,ServerSocket, etc.)jdk.MethodTrace,jdk.MethodTiming: Method tracing/timing events (Introduced in JDK 25, instrumented via method filters)
- Advantages: Can monitor execution of JDK libraries and application code without modifying source code
- Applicable scenarios: I/O events, method tracing events, events that need to monitor application code
Sampling-based Events
- Characteristics: Independent sampling thread periodically samples target thread states, generates events
- Implementation location:
- Sampler implementation:
src/hotspot/share/jfr/recorder/checkpoint/types/jfrThreadSampler.cpp - Sampling thread:
JfrThreadSamplerthread
- Sampler implementation:
- Implementation mechanism:
- Sampling thread periodically wakes up (default interval controlled by
samplethreadsconfiguration) - Traverse all Java threads, get thread state and stack traces
- Decide whether to generate events based on event configuration
- Sampling thread periodically wakes up (default interval controlled by
- Typical events:
jdk.ExecutionSample: Method execution sampling events (stack traces of sampled threads)jdk.NativeMethodSample: Native method sampling events (sample native method calls)
- Advantages: Low overhead, no need to instrument business code, minimal performance impact
- Applicable scenarios: Events that need periodic sampling of thread states, such as performance analysis events
Hybrid Implementation Events
- Characteristics: Events that combine multiple implementation mechanisms
- Typical events:
jdk.ObjectAllocationSample:- Allocation path trigger: Triggered via C++ code in object allocation path (similar to
jdk.ObjectAllocationInNewTLAB) - Sampling mechanism: Control collection frequency through sampling mechanism (via
throttleconfiguration)
- Allocation path trigger: Triggered via C++ code in object allocation path (similar to
jdk.OldObjectSample:- GC trigger: Identify old objects via C++ code during GC process
- Reference chain lookup: Find GC root paths at Java layer (implemented via Java code)
- Advantages: Combine advantages of different implementation mechanisms, implement complex event collection logic
2. JFR Quick Start#
First, prepare the following tools or environment:
- Java 25
- JDK Mission Control (JMC) 8.3+
- Jmeter
- Code repository: https://github.com/spring-projects/spring-petclinic.git
- maven or gradle
After cloning the code repository, we use maven to build:
cd spring-petclinic
mvn clean package
Then, we start the spring-petclinic application with the following JVM parameters:
-XX:StartFlightRecording=disk=true
-XX:FlightRecorderOptions=maxchunksize=128m,repository=./,stackdepth=256,preserve-repository=true
The configuration here means:
-XX:StartFlightRecording=disk=true: Start a JFR recording via JVM parameter StartFlightRecording, and write data to disk (i.e., the repository directory mentioned earlier). Later we will see that multipleStartFlightRecordingparameters can be used to start multiple recordings.-XX:FlightRecorderOptions=maxchunksize=128m,repository=./,stackdepth=256,preserve-repository=true: Configure JFR’s global options, here configuredchunksize as 128MB,repositorydirectory as current directory, stack depth as 256,preserve-repository=truemeans preserve chunk files in repository directory (by default, JFR will delete chunk files in repository directory on JVM exit).
After startup, we can see the repository directory with the following structure in the startup directory (we specified as current directory):
├── 2025_11_19_17_41_00_3833
│ ├── 2025_11_19_17_41_00.jfr
The folder name is in <yyyy_MM_dd_HH_mm_ss_pid> format, representing JVM startup time and process ID, and the *.jfr files inside are JFR recording chunk files, filename is in <yyyy_MM_dd_HH_mm_ss>.jfr format, representing chunk generation time.
Use jfr command to view file contents:
% jfr summary ./2025_11_19_17_41_00_3833/2025_11_19_17_41_00.jfr
Version: 2.1
Chunks: 1
Start: 2025-11-17 09:41:00 (UTC)
Duration: 165 s
Event Type Count Size (bytes)
=============================================================
jdk.NativeMethodSample 6992 79112
jdk.GCPhaseParallel 6127 150540
jdk.ModuleExport 1615 19253
jdk.SystemProcess 1032 119761
jdk.NativeLibrary 877 76574
......
If your process is running, this command may indicate the jfr file is corrupted (when you’re viewing the last file in the repository), this is because metadata hasn’t been flushed yet, don’t worry, flush is executed every 1s by default, try a few more times and you’ll see correct results.
To view details of a specific event, use jfr print command:
% jfr print --events jdk.CPULoad ./2025_11_19_17_41_00_3833/2025_11_19_17_41_00.jfr
jdk.CPULoad {
startTime = 17:41:02.893 (2025-11-19)
jvmUser = 7.24%
jvmSystem = 0.93%
machineTotal = 20.19%
}
jdk.CPULoad {
startTime = 17:41:03.898 (2025-11-19)
jvmUser = 0.81%
jvmSystem = 0.25%
machineTotal = 14.56%
}
3. Using JFR via JVM Parameters#
Using JFR via JVM parameters is the most common method, allowing direct configuration and starting of JFR recordings at JVM startup. JFR provides two main JVM parameters:
-XX:+FlightRecorder: Introduced in JDK 11, default value: false, this configuration is deprecated, and although the default value is false, if not set, the JVM still sets it to true, i.e., JFR is enabled by default, JFR functionality is only disabled when explicitly set to false-XX:FlightRecorderOptions: Configure JFR’s global options-XX:StartFlightRecording: Start one or more JFR recordings
3.1. -XX:FlightRecorderOptions (Global Configuration)#
-XX:FlightRecorderOptions is used to configure JFR’s global options, these options affect the behavior of all JFR recordings.
Syntax:
-XX:FlightRecorderOptions=<option1>=<value1>,<option2>=<value2>,...
Supported Options (see Section 1.3 for detailed configuration analysis):
repository=<path>: Repository path (default: temporary directory)globalbuffersize=<size>: Global buffer size (default: 512k)numglobalbuffers=<count>: Number of global buffers (default: 20)memorysize=<size>: Total memory limit for JFR (default: 10m)threadbuffersize=<size>: Thread buffer size (default: 8k)maxchunksize=<size>: Maximum chunk size (default: 12m)stackdepth=<depth>: Stack trace depth (default: 64)retransform=<true|false>: Whether to allow JVMTI retransform (default: true)old-object-queue-size=<size>: OldObjectSample queue size (default: 256)dumppath=<path>: Emergency dump path (JDK 17+, default: current working directory)preserve-repository=<true|false>: Whether to preserve Repository on JVM exit (JDK 21+, default: false)sampleprotection=<true|false>: Protection measures for stack traversal when sampling threads (DEBUG mode only)
Examples:
# Configure global options
-XX:FlightRecorderOptions=maxchunksize=128m,repository=./,stackdepth=256,preserve-repository=true
# Configure memory-related options
-XX:FlightRecorderOptions=memorysize=50m,globalbuffersize=1m,numglobalbuffers=50
# Configure buffer sizes
-XX:FlightRecorderOptions=threadbuffersize=16k,globalbuffersize=1m
Notes:
- Format requirements: Options are separated by commas (
,), option names and values are separated by equals (=) - Size units: Memory sizes support
k/K(KB),m/M(MB),g/G(GB) - Path format: Paths can be absolute or relative (relative to current working directory)
- Runtime modification: Some options (such as
repository,maxchunksize) can be modified at runtime viajcmd JFR.configurecommand
3.2. -XX:StartFlightRecording (Start Recording)#
-XX:StartFlightRecording is used to start one or more JFR recordings at JVM startup. This parameter can be specified multiple times to start multiple independent recordings.
Syntax:
-XX:StartFlightRecording[=<parameter1>=<value1>,<parameter2>=<value2>,...]
Parameter format:
- Parameters are separated by commas (
,) - Parameter names and values are separated by equals (
=) - If no parameters are specified, defaults to
dumponexit=false(i.e., no automatic dump)
Supported parameters:
3.2.1. Basic Parameters#
name=<name> (optional):
- Function: Specifies recording identifier name
- Default value: System auto-generated (format:
Recording <id>) - Limitation: Name cannot be pure numbers to avoid confusion with recording ID
- Examples:
name=MyRecording,name=Production-Monitoring
settings=<jfc-files> (optional):
- Function: Specifies JFC configuration file (see Chapter 5 for details)
- Default value:
default.jfc - Format:
- Single file:
settings=default,settings=profile,settings=/path/to/custom.jfc - Multiple files (merged):
settings=default,settings=profile(separated by commas, can be repeated) - No configuration:
settings=none
- Single file:
- Examples:
settings=defaultsettings=profilesettings=default,settings=/path/to/custom.jfcsettings=none
disk=<true|false> (optional):
- Function: Controls whether recording is written to disk
- Default value:
true - Description:
true: Data continuously written to disk repository, supportsmaxageandmaxsizelimitsfalse: Data only stored in memory, suitable for short-term recordings
- Examples:
disk=true,disk=false
3.2.2. Time-related Parameters#
delay=<time> (optional):
- Function: Delay start time
- Default value:
0s(start immediately) - Format: Integer followed by unit (
s=seconds,m=minutes,h=hours,d=days) - Minimum value:
1s - Examples:
delay=5m(delay 5 minutes),delay=1h(delay 1 hour)
duration=<time> (optional):
- Function: Auto-stop time
- Default value:
0s(unlimited) - Format: Integer followed by unit (
s=seconds,m=minutes,h=hours,d=days) - Minimum value:
1s - Description: When specified time is reached, recording automatically stops; if
filenameis also specified, recording is automatically dumped after stopping - Examples:
duration=1h(record for 1 hour),duration=30m(record for 30 minutes)
maxage=<time> (optional):
- Function: Maximum retention time (only effective when
disk=true) - Default value:
0s(unlimited) - Format: Integer followed by unit (
s=seconds,m=minutes,h=hours,d=days) - Description: Old chunk files exceeding specified time are automatically deleted
- Examples:
maxage=2d(retain 2 days),maxage=24h(retain 24 hours)
flush-interval=<time> (optional, JDK 17+):
- Function: Periodic flush interval (only effective when
disk=true) - Default value:
1s - Format: Integer followed by unit (
s=seconds,m=minutes,h=hours,d=days) - Description:
0means only flush when recording ends - Examples:
flush-interval=5s,flush-interval=0(only flush at end)
3.2.3. File-related Parameters#
filename=<path> (optional):
- Function: Specifies file path to dump when recording stops
- Default value: System auto-generated (format:
hotspot-pid-<pid>-id-<id>-<timestamp>.jfr) - Format:
- File path:
filename=/path/to/recording.jfr - Directory path:
filename=/path/to/directory(filename will be generated in that directory) - Placeholders:
filename=recording-%p-%t.jfr(%p=PID,%t=timestampyyyy_MM_dd_HH_mm_ss)
- File path:
- Description: If
filenameis specified,dumponexit=trueby default - Examples:
filename=recording.jfrfilename=./recordings/filename=recording-%p-%t.jfr
dumponexit=<true|false> (optional):
- Function: Whether to dump on JVM exit
- Default value:
false(iffilenameis specified, thentrue) - Description: When
true, recording is automatically dumped when JVM exits normally or abnormally - Example:
dumponexit=true
3.2.4. Other Parameters#
maxsize=<size> (optional):
- Function: Maximum total size (only effective when
disk=true) - Default value:
0(unlimited, but ifduration,maxage,maxsizeare all unspecified, defaults to250MB) - Format: Integer followed by unit (
k/K=KB,m/M=MB,g/G=GB) - Limitation: Value cannot be less than
maxchunksizein global configuration - Examples:
maxsize=500M,maxsize=1G
path-to-gc-roots=<true|false> (optional):
- Function: Whether to record GC roots
- Default value:
false - Description: When
true, GC root path information for objects in OldObjectSample events is collected at the end of recording, used for finding memory leaks - Example:
path-to-gc-roots=true
report-on-exit=<view-names> (optional, JDK 25+):
- Function: Generate report view(s) when JVM exits
- Default value: None (no report generated)
- Format: Can repeat to specify multiple views, separated by commas
- Description: Only effective when
disk=true - Example:
report-on-exit=jvm-information,report-on-exit=system-properties
3.2.5. Event Configuration Parameters#
In addition to the above parameters, event configurations can also be directly specified in -XX:StartFlightRecording:
JFC Options (<option>=<value>):
- Format:
<jfc-option>=<value> - Description: Modify options in JFC file (such as
gc=high,method-profiling=high) - Examples:
gc=high,method-profiling=high
Event Settings (<event-name>#<setting-name>=<value>):
- Format:
<event-name>#<setting-name>=<value> - Description: Directly modify event configuration items
- Add new event: Use
+prefix, such as+jdk.CustomEvent#enabled=true - Examples:
jdk.ThreadSleep#threshold=50msjdk.CPULoad#period=2s+jdk.CustomEvent#enabled=true
Time interval format:
- Time values can omit spaces, such as
20msis equivalent to20 ms - Supported units:
ns(nanoseconds),us(microseconds),ms(milliseconds),s(seconds),m(minutes),h(hours),d(days)
3.3. Usage Examples#
3.3.1. Basic Usage#
# Simplest usage (using default configuration)
-XX:StartFlightRecording
# Use predefined configuration
-XX:StartFlightRecording=settings=default
# Use custom configuration and specify filename
-XX:StartFlightRecording=settings=profile,filename=recording.jfr
# Specify recording name and filename
-XX:StartFlightRecording=name=MyRecording,filename=myrecording.jfr
3.3.2. Time-limited Recordings#
# Record for 1 hour then automatically stop and dump
-XX:StartFlightRecording=duration=1h,filename=recording.jfr
# Delay 5 minutes before starting, record for 30 minutes
-XX:StartFlightRecording=delay=5m,duration=30m,filename=recording.jfr
# Record for 1 hour, but retain data from last 2 days
-XX:StartFlightRecording=duration=1h,maxage=2d,maxsize=5GB
3.3.3. Memory Recordings (not written to disk)#
# Memory recording, only dump on exit
-XX:StartFlightRecording=disk=false,dumponexit=true,filename=recording.jfr
# Memory recording, manual dump (via jcmd)
-XX:StartFlightRecording=disk=false
3.3.4. Event Configuration Override#
# Use default.jfc, but increase lock wait threshold
-XX:StartFlightRecording=settings=default,jdk.ThreadSleep#threshold=50ms
# Use profile.jfc, but disable method sampling
-XX:StartFlightRecording=settings=profile,jdk.ExecutionSample#enabled=false
# Use JFC options (can see Control section in jfc, Selection values can be modified via the following way), Chapter 5 will detail available options
-XX:StartFlightRecording=settings=default,gc=high,method-profiling=high
# Configure custom events
-XX:StartFlightRecording=settings=none,+jdk.CustomEvent#enabled=true,+jdk.CustomEvent#stackTrace=true
3.3.5. Multiple Recordings (JDK 11+)#
Important: -XX:StartFlightRecording can be specified multiple times to start multiple independent recordings, each recording has independent configuration and lifecycle.
# Start two recordings: one for long-term monitoring, one for short-term analysis
-XX:StartFlightRecording=name=LongTerm,maxage=2d,maxsize=5GB
-XX:StartFlightRecording=name=ShortTerm,maxage=10m,dumponexit=true,filename=shortterm.jfr
# Start three recordings with different configurations
-XX:StartFlightRecording=name=Recording1,filename=recording1.jfr
-XX:StartFlightRecording=name=Recording2,filename=recording2.jfr,settings=profile
-XX:StartFlightRecording=name=Recording3,disk=false
# Mixed use of colon and equals (both supported)
-XX:StartFlightRecording:name=myrecording1,filename=myrecording1.jfr
-XX:StartFlightRecording=name=myrecording2,filename=myrecording2.jfr
Use cases for multiple recordings: Long-term monitoring + Short-term analysis:
- One recording for long-term monitoring (
maxage=2d, maxsize=5GB), retain historical data - Another recording for short-term analysis (
maxage=10m, dumponexit=true), dump on exit
3.3.6. Complete Examples#
# Production environment configuration example
java \
-XX:FlightRecorderOptions=maxchunksize=128m,repository=./,stackdepth=256,preserve-repository=true \
-XX:StartFlightRecording=name=Production,settings=default,maxage=3d,maxsize=10GB \
-XX:StartFlightRecording=name=Debug,settings=profile,duration=1h,filename=debug.jfr \
-jar MyApp.jar
# Performance analysis configuration example
java \
-XX:FlightRecorderOptions=stackdepth=128 \
-XX:StartFlightRecording=settings=profile,gc=high,method-profiling=high,duration=30m,filename=profile.jfr \
-jar MyApp.jar
# Memory leak analysis configuration example
java \
-XX:StartFlightRecording=settings=default,path-to-gc-roots=true,duration=1h,filename=leak-analysis.jfr \
-jar MyApp.jar
3.4. View Help Information#
You can view detailed help information for -XX:StartFlightRecording in the following way:
# View help information (prints all available parameters and examples)
java -XX:StartFlightRecording:help
4. Using JFR via jcmd Commands#
jcmd is a diagnostic command tool provided by JDK that can manage JVM processes via command line at runtime. JFR provides multiple jcmd subcommands to manage JFR recordings and configurations.
Basic syntax:
jcmd <pid> <command> [options]
Where:
<pid>: Process ID of target JVM process<command>: JFR command (such asJFR.start,JFR.stop, etc.)[options]: Command parameters (separated by spaces, format:key=value)
View all available JFR commands:
# View all jcmd commands
jcmd <pid> help
# View help for JFR-related commands
jcmd <pid> help JFR.start
jcmd <pid> help JFR.stop
jcmd <pid> help JFR.dump
jcmd <pid> help JFR.check
jcmd <pid> help JFR.configure
jcmd <pid> help JFR.view
4.1. JFR.start (Start Recording)#
JFR.start command is used to start a new JFR recording at runtime.
Syntax:
jcmd <pid> JFR.start [options]
Important: Unlike -XX:StartFlightRecording, parameters in jcmd JFR.start are separated by spaces, not commas.
Supported parameters (same as -XX:StartFlightRecording, only difference is separator, see Section 3.2 for details):
name=<name>: Recording namesettings=<jfc-files>: JFC configuration file(s) (multiple files separated by commas, can be repeated)disk=<true|false>: Whether to write to disk (default:true)delay=<time>: Delay start timeduration=<time>: Auto-stop timefilename=<path>: Dump filenamemaxage=<time>: Maximum retention time (only effective whendisk=true)maxsize=<size>: Maximum total size (only effective whendisk=true)flush-interval=<time>: Periodic flush interval (JDK 17+, only effective whendisk=true)dumponexit=<true|false>: Whether to dump on JVM exitpath-to-gc-roots=<true|false>: Whether to record GC rootsreport-on-exit=<view-names>: Generate report view(s) when JVM exits (JDK 25+, only effective whendisk=true)- JFC options:
<option>=<value>(such asgc=high,method-profiling=high) - Event settings:
<event-name>#<setting-name>=<value>(such asjdk.ThreadSleep#threshold=50ms)
Usage examples:
# Start recording with default configuration
jcmd <pid> JFR.start
# Start recording with predefined configuration
jcmd <pid> JFR.start settings=default
# Specify recording name and filename
jcmd <pid> JFR.start name=MyRecording filename=recording.jfr
# Start time-limited recording
jcmd <pid> JFR.start duration=1h filename=recording.jfr
# Start memory recording (not written to disk)
jcmd <pid> JFR.start disk=false
# Use profile.jfc and override event configuration
jcmd <pid> JFR.start settings=profile jdk.ThreadSleep#threshold=50ms
# Use JFC options
jcmd <pid> JFR.start settings=default gc=high method-profiling=high
# Delay start and specify duration
jcmd <pid> JFR.start delay=5m duration=30m filename=recording.jfr
# Start recording and configure retention policy
jcmd <pid> JFR.start maxage=2d maxsize=5GB
# Start recording and enable GC root path collection
jcmd <pid> JFR.start path-to-gc-roots=true duration=1h filename=leak-analysis.jfr
Notes:
- Parameter separator: Parameters are separated by spaces (unlike JVM parameters which use commas)
- Parameter format: Parameter names and values are separated by equals (
=) - Multiple starts: Can execute
JFR.startmultiple times to start multiple independent recordings - Recording ID: After successful start, recording ID is displayed for subsequent management
4.2. JFR.stop (Stop Recording)#
JFR.stop command is used to stop a running JFR recording.
Syntax:
jcmd <pid> JFR.stop name=<name> [filename=<path>]
Parameters:
name=<name>(required): Recording identifier name or IDfilename=<path>(optional): File path to dump when stopping- If
filenameis specified, it will override the dump path set at start, recording will be dumped to newly specified file after stopping - If
filenameis not specified:- If
filenamewas specified at start (file path or directory path), recording will automatically dump to location specified at start after stopping - If
filenamewas not specified at start, recording data will be discarded
- If
- Note:
filenamemust specify complete file path, directory paths are not supported (unlikeJFR.startandJFR.dump) - Supports placeholders:
%p(PID),%t(timestampyyyy_MM_dd_HH_mm_ss)
- If
Usage examples:
# Stop recording (if dump path was not specified at start, no dump, data discarded)
jcmd <pid> JFR.stop name=1
# Stop recording (if filename was specified at start, automatically dump to location specified at start)
jcmd <pid> JFR.stop name=MyRecording
# Stop recording and dump to new file (override dump path set at start)
jcmd <pid> JFR.stop name=MyRecording filename=recording.jfr
# Use placeholders to generate filename
jcmd <pid> JFR.stop name=1 filename=recording-%p-%t.jfr
Notes:
- Recording identifier: Can use recording name or recording ID (number) to identify recording
- Data save behavior:
- If
filenameis specified inJFR.stop, it overrides dump path set at start, dumps to newly specified file - If
filenameis not specified inJFR.stop, but was specified at start, automatically dumps to location specified at start (file path or directory path) - If
filenameis not specified at both start and stop, recording data will be discarded and cannot be recovered
- If
- File path limitation:
filenameparameter must specify complete file path, cannot be directory. If directory needs to be specified, should useJFR.dumpcommand orfilenameparameter ofJFR.startcommand (they support directory paths)
4.3. JFR.dump (Dump Recording)#
JFR.dump command is used to dump a running recording to file, recording continues running.
Syntax:
jcmd <pid> JFR.dump [options]
Parameters:
name=<name>(optional): Recording identifier name or ID- If specified, only dump specified recording
- If not specified, dump all running recordings
filename=<path>(optional): Dump filename- If not specified, system auto-generates filename
- Can specify file path or directory path (if directory is specified, filename will be auto-generated in that directory)
- Supports placeholders:
%p(PID),%t(timestamp)
maxage=<time>(optional): Time range to dump (from current time backwards)- Format: Integer followed by unit (
s=seconds,m=minutes,h=hours,d=days) - Example:
maxage=1h(dump data from last 1 hour) - Note: Cannot be used together with
beginorend
- Format: Integer followed by unit (
maxsize=<size>(optional): Size limit for dump- Format: Integer followed by unit (
k/K=KB,m/M=MB,g/G=GB) - Example:
maxsize=500M
- Format: Integer followed by unit (
begin=<time>(optional): Start time- Supported formats:
- ISO 8601 format:
2020-03-17T09:00:00,2020-03-17T09:00:00Z - Local time:
13:20:15,2020-03-17T09:00:00 - Relative time:
-12h(12 hours ago),-15m(15 minutes ago),-30s(30 seconds ago)
- ISO 8601 format:
- Note: Cannot be used together with
maxage
- Supported formats:
end=<time>(optional): End time- Format same as
begin - Note:
endmust be later thanbegin
- Format same as
path-to-gc-roots=<true|false>(optional): Whether to collect GC root paths- Default value:
false - Only enable when memory leak analysis is needed
- Default value:
Usage examples:
# Dump all recordings (using auto-generated filename)
jcmd <pid> JFR.dump
# Dump specified recording to file
jcmd <pid> JFR.dump name=1 filename=recording.jfr
# Dump data from last 1 hour
jcmd <pid> JFR.dump name=1 maxage=1h filename=recording.jfr
# Dump data from last 1 hour with maximum 500MB
jcmd <pid> JFR.dump name=1 maxage=1h maxsize=500M filename=recording.jfr
# Dump data from specified time range
jcmd <pid> JFR.dump name=1 begin=13:15 end=21:30:00 filename=recording.jfr
# Dump data from specified date-time range
jcmd <pid> JFR.dump name=1 begin=2021-09-15T09:00:00 end=2021-09-15T10:00:00 filename=recording.jfr
# Dump data from relative time range
jcmd <pid> JFR.dump name=1 begin=-1h end=-5m filename=recording.jfr
# Dump and collect GC root paths (for memory leak analysis)
jcmd <pid> JFR.dump name=1 filename=leaks.jfr path-to-gc-roots=true
# Dump to directory (auto-generate filename)
jcmd <pid> JFR.dump name=1 filename=./recordings/
Time format notes:
ISO 8601 format:
2020-03-17T09:00:00(local time)2020-03-17T09:00:00Z(UTC time)
Local time format:
13:20:15(today’s time)2020-03-17T09:00:00(specified date-time)
Relative time format:
-12h: 12 hours ago-15m: 15 minutes ago-30s: 30 seconds ago-1d: 1 day ago
Notes:
- Recording continues running:
JFR.dumpdoes not stop recording, recording continues running - Time range:
maxagecannot be used together withbegin/end - Data filtering: Dumped data will be filtered based on
maxage,maxsize,begin,end - GC root paths: Collecting GC root paths will cause brief application pause, only enable when needed
4.4. JFR.check (Check Recording)#
JFR.check command is used to view information about running JFR recordings.
Syntax:
jcmd <pid> JFR.check [name=<name>] [verbose=<true|false>]
Parameters:
name=<name>(optional): Recording identifier name or ID- If specified, only show information for specified recording
- If not specified, show information for all running recordings
verbose=<true|false>(optional): Whether to show detailed event configuration- Default value:
false - When
true, shows detailed configuration for each event (enabled,threshold,period, etc.)
- Default value:
Usage examples:
# View basic information for all recordings
jcmd <pid> JFR.check
# View detailed information for all recordings (including event configuration)
jcmd <pid> JFR.check verbose=true
# View basic information for specified recording
jcmd <pid> JFR.check name=1
# View detailed information for specified recording
jcmd <pid> JFR.check name=MyRecording verbose=true
Output examples:
# Basic output
$ jcmd <pid> JFR.check
Recording 1: name=MyRecording duration=1h maxsize=500M maxage=2d (running)
# Detailed output (verbose=true)
$ jcmd <pid> JFR.check name=1 verbose=true
Recording 1: name=MyRecording duration=1h maxsize=500M maxage=2d (running)
Thread Sleep (jdk.ThreadSleep)
[enabled=true, threshold=20 ms, stackTrace=true]
CPU Load (jdk.CPULoad)
[enabled=true, period=1 s]
Java Monitor Enter (jdk.JavaMonitorEnter)
[enabled=true, threshold=20 ms, stackTrace=true]
Notes:
- Recording status: Output shows recording status (
running,stopped, etc.) - Recording information: Includes recording ID, name, duration, maximum size, maximum retention time, etc.
- Event configuration: When
verbose=true, shows detailed settings for all configured events
4.5. JFR.configure (Configure Global Options)#
JFR.configure command is used to configure JFR’s global options at runtime.
Syntax:
jcmd <pid> JFR.configure [options]
Supported options (same as -XX:FlightRecorderOptions, see Section 1.3 for details):
repositorypath=<path>: Repository pathdumppath=<path>: Emergency dump path (JDK 17+)stackdepth=<depth>: Stack trace depth (can only be modified before JFR initialization)globalbuffercount=<count>: Number of global buffers (can only be modified before JFR initialization, legacy option)globalbuffersize=<size>: Global buffer size (can only be modified before JFR initialization, legacy option)thread_buffer_size=<size>: Thread buffer size (can only be modified before JFR initialization)memorysize=<size>: Total memory limit for JFR (can only be modified before JFR initialization)maxchunksize=<size>: Maximum chunk size (can only be modified before JFR initialization)preserve-repository=<true|false>: Whether to preserve Repository on JVM exit (JDK 21+)
Usage examples:
# View current configuration
jcmd <pid> JFR.configure
# Modify Repository path
jcmd <pid> JFR.configure repositorypath=/tmp/jfr
# Note: Old repository data will remain at original location, will not be migrated
# If old repository directory is already in cleanupDirectories, JFR will continue to track it and automatically delete it on JVM shutdown (if preserve-repository=false)
# If there are running disk recordings, current chunk will be marked as final and rotated, new chunks will be written to new repository path
# Modify emergency dump path
jcmd <pid> JFR.configure dumppath=/tmp/emergency
# Modify stack depth (only effective before JFR initialization)
jcmd <pid> JFR.configure stackdepth=128
# Modify memory size (only effective before JFR initialization)
jcmd <pid> JFR.configure memorysize=50M
# Modify maximum chunk size (only effective before JFR initialization)
jcmd <pid> JFR.configure maxchunksize=128M
# Enable preserve Repository
jcmd <pid> JFR.configure preserve-repository=true
# Modify multiple options simultaneously
jcmd <pid> JFR.configure repositorypath=/tmp/jfr dumppath=/tmp/emergency preserve-repository=true
Notes:
- Initialization limitation: Some options (such as
stackdepth,memorysize,maxchunksize) can only be modified before JFR initialization, if JFR is already initialized, these options cannot be modified - Runtime modification:
repositorypath,dumppath,preserve-repositorycan be modified at runtime - Legacy options:
globalbuffercountandglobalbuffersizeare legacy options, recommend usingmemorysizefor unified adjustment - View configuration: When no parameters are specified, shows current configuration
- Impact of Repository path modification:
- When modifying
repositorypath, old repository directory and chunk files in it will remain at original location, will not be migrated. If old repository directory is already incleanupDirectories(i.e., previously created vianewChunk()), JFR will continue to track it and automatically delete it on JVM shutdown (ifpreserve-repository=false) - If there are running disk recordings, current chunk will be marked as final and rotated, new chunks will be written to new repository path
- New repository directory will be created under new path (format:
<base-path>/<timestamp>_<pid>or<base-path>/<timestamp>_<pid>_<index>) - Cleanup behavior of old repository directory:
- JFR internally uses
cleanupDirectories(Set<Path>) to track repository directories that need to be cleaned up on JVM shutdown - Only when new repository directory is created via
Repository.newChunk(), that directory is added tocleanupDirectories - If old repository directory was already added to
cleanupDirectoriesbefore becoming “old” (vianewChunk()), it remains incleanupDirectoriesand will be cleaned up on JVM shutdown (ifpreserve-repository=false) - If old repository directory was never added to
cleanupDirectories(e.g., repository path was set before JFR initialization, but chunk was never created), it will not be automatically cleaned up on JVM shutdown - If there are running disk recordings after path modification,
migrate()will callrotateDisk()→newChunk(), at this point new repository directory will be created and added tocleanupDirectories - On JVM shutdown, if
preserve-repository=false, all directories incleanupDirectorieswill be cleaned up (Repository.clear()method, lines 173-180) - Note:
cleanupDirectoriesis an automatically managed mechanism by JFR, users cannot directly add directories to this set
- JFR internally uses
- When modifying
4.6. JFR.view (View Recording)#
JFR.view command is used to display JFR recording data in predefined view format, no need to dump file.
Syntax:
jcmd <pid> JFR.view <view> [options]
Parameters:
<view>(required): View name or event type name- Predefined views:
gc,hot-methods,allocation-by-class,contention-by-site, etc.- Note: Views are defined in
view.iniusing full names (such as[jvm.gc],[application.hot-methods]), but inJFR.viewcommand only need to use view name part, no need to add prefix. For example:- Use
jcmd <pid> JFR.view gcinstead ofjcmd <pid> JFR.view jvm.gc - Use
jcmd <pid> JFR.view hot-methodsinstead ofjcmd <pid> JFR.view application.hot-methods
- Use
- View name matching is case-insensitive (
equalsIgnoreCase)
- Note: Views are defined in
- Event types:
jdk.GarbageCollection,jdk.ThreadStart, etc.- Can directly use event type name to view all data for that event
- Special values:
types: List all available event typesall-views: Display all predefined viewsall-events: Display all events
- Predefined views:
maxage=<time>(optional): Time range to view (default:10m)- Format: Integer followed by unit (
s=seconds,m=minutes,h=hours,d=days)
- Format: Integer followed by unit (
maxsize=<size>(optional): Size limit to view (default:32MB)- Format: Integer followed by unit (
m/M=MB,g/G=GB)
- Format: Integer followed by unit (
width=<integer>(optional): View width (character count, default:100)truncate=<beginning|end>(optional): Truncation mode (default:end)beginning: Truncate from beginningend: Truncate from end
cell-height=<integer>(optional): Maximum number of lines for table cellsverbose=<true|false>(optional): Whether to display view query information (default:false)
Usage examples:
# View GC view
jcmd <pid> JFR.view gc
# View hot methods view
jcmd <pid> JFR.view hot-methods
# View allocation view (by class)
jcmd <pid> JFR.view allocation-by-class
# View lock contention view (by site)
jcmd <pid> JFR.view contention-by-site
# View specified event type
jcmd <pid> JFR.view jdk.GarbageCollection
# View all available views
jcmd <pid> JFR.view all-views
# View all event types
jcmd <pid> JFR.view types
# Custom view parameters
jcmd <pid> JFR.view width=160 hot-methods
# View data from last 1 hour
jcmd <pid> JFR.view maxage=1h gc
# Display view query information
jcmd <pid> JFR.view verbose=true allocation-by-class
# Set truncation mode
jcmd <pid> JFR.view truncate=beginning SystemProcess
Predefined Views Details:
JFR provides rich predefined views, these views are defined in view.ini configuration file, using custom query language to aggregate and display event data. Views are divided into three categories: JVM Views (jvm.*), Application Views (application.*), and Environment Views (environment.*).
view.ini file location:
- Source location:
src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini - Compiled location:
view.inifile is packaged intojdk.jfrmodule’s JAR file (usually inlib/jfr.jarorjmods/jdk.jfr.jmod), exists as resource file - Runtime access: Read at runtime via
ViewFile.class.getResourceAsStream("/jdk/jfr/internal/query/view.ini") - File format: INI format configuration file, contains view definitions and query statements
- User access: Users can view
view.inifile content by extractingjdk.jfrmodule’s JAR file, but not recommended to modify directly
View structure:
Each view definition contains the following parts:
- View name: Format is
<category>.<name>, for examplejvm.gc,application.hot-methods - Label: Display name of view
- Query type:
table: Table view, used to display multiple rows of data, supports sorting, grouping, aggregationform: Form view, used to display single row summary data, usually usesLAST()function to get latest value
Query language syntax:
Views use SQL-like query language, supports the following syntax:
- COLUMN: Define column headers
- FORMAT: Define column format (
none,normalized,cell-height,truncate-beginning/end,missing:whitespace, etc.) - SELECT: Select fields and aggregation functions
- FROM: Specify event types (supports union of multiple event types)
- WHERE: Filter conditions
- GROUP BY: Group aggregation
- ORDER BY: Sorting (
ASC/DESC) - LIMIT: Limit result count
Supported aggregation functions:
COUNT(*): CountSUM(field): SumAVG(field): AverageMIN(field): MinimumMAX(field): MaximumMEDIAN(field): MedianP90/P95/P99/P999(field): PercentilesFIRST(field): First valueLAST(field): Last valueLAST_BATCH(field): Last batch of values (same end timestamp)DIFF(field): Difference (last value minus first value)STDEV(field): Standard deviationLIST(field): Comma-separated list of all valuesSET(field): Comma-separated list of all unique valuesUNIQUE(field): Number of unique values
Stack trace access:
Query language supports accessing specific frames of stack traces:
stackTrace.topFrame: Top frame of stack (outermost method)stackTrace.topApplicationFrame: Top application frame of stack (excluding JDK internal methods)stackTrace.topNotInitFrame: Top non-initialization frame of stack (excluding<init>methods)
Common predefined view categories:
JVM Views (jvm.*):
gc: Garbage collection statistics- Events:
GarbageCollection,GCHeapSummary - Design: Table view, displays start time, GC ID, GC name, heap usage before/after GC, longest pause time for each GC
- Aggregation: Group by
gcId, associate heap summary data before/after GC
- Events:
gc-pauses: GC pause statistics- Events:
GCPhasePause - Design: Form view, displays total pause time, pause count, min/median/avg/P90/P95/P99/P99.9/max pause time
- Aggregation: Uses
SUM,COUNT,MIN,MEDIAN,AVG,P90/P95/P99/P999,MAXfunctions
- Events:
gc-configuration: GC configuration information- Events:
GCConfiguration - Design: Form view, displays young/old generation collectors, GC thread count, explicit GC settings, etc.
- Aggregation: Uses
LAST()to get latest configuration values
- Events:
gc-parallel-phases: Parallel GC phases- Events:
GCPhaseParallel - Design: Table view, grouped by phase name, displays average, P95, longest, count, total time
- Events:
gc-concurrent-phases: Concurrent GC phases- Events:
GCPhaseConcurrent,GCPhaseConcurrentLevel1 - Design: Table view, grouped by phase name, displays average, P95, longest, count, total time
- Events:
gc-pause-phases: GC pause phases- Events:
GCPhasePause,GCPhasePauseLevel1-4 - Design: Table view, grouped by phase name, displays event type, average, P95, longest, count, total time
- Events:
gc-references: GC reference statistics- Events:
GCReferenceStatistics - Design: Table view, grouped by GC ID, displays soft/weak/phantom/finalizer reference counts
- Events:
gc-allocation-trigger: GC allocation trigger- Events:
AllocationRequiringGC - Design: Table view, grouped by application method, displays trigger count and total requested size
- Stack: Uses
stackTrace.topApplicationFrameto get application layer method
- Events:
gc-cpu-time: GC CPU time- Events:
GCCPUTime - Design: Form view, displays GC user time, system time, wall clock time, total time, GC count
- Events:
heap-configuration: Heap configuration- Events:
GCHeapConfiguration - Design: Form view, displays initial size, min size, max size, compressed OOPs settings
- Events:
compiler-configuration: Compiler configuration- Events:
CompilerConfiguration - Design: Form view, displays compiler thread count, dynamic thread count, tiered compilation settings
- Events:
compiler-statistics: Compiler statistics- Events:
CompilerStatistics - Design: Form view, displays compilation count, peak time, total time, deoptimization count, OSR/standard compilation statistics, etc.
- Events:
compiler-phases: Compiler phases- Events:
CompilerPhase - Design: Table view, grouped by compilation level and phase, displays average, P95, longest, count, total time
- Events:
longest-compilations: Longest compilations- Events:
Compilation - Design: Table view, displays top 25 methods with longest compilation time
- Events:
safepoints: Safepoints- Events:
SafepointBegin,SafepointEnd,SafepointStateSynchronization - Design: Table view, grouped by safepoint ID, displays start time, duration, state synchronization time, JNI critical thread count, total thread count
- Events:
vm-operations: VM operations- Events:
jdk.ExecuteVMOperation - Design: Table view, grouped by operation type, displays average duration, longest duration, count, total duration
- Events:
deoptimizations-by-reason: Deoptimizations by reason- Events:
Deoptimization - Design: Table view, grouped by deoptimization reason, displays count
- Events:
deoptimizations-by-site: Deoptimizations by site- Events:
Deoptimization - Design: Table view, grouped by method, line number, BCI, displays count
- Events:
class-modifications: Class modifications- Events:
RetransformClasses,RedefineClasses - Design: Table view, grouped by redefinition ID, displays duration, requester (application method), operation type, class count
- Stack: Uses
stackTrace.topApplicationFrameto get requester
- Events:
blocked-by-system-gc: Blocked by System.gc()- Events:
SystemGC - Design: Table view, filters non-concurrent calls, displays start time, duration, stack trace
- Events:
native-memory-committed: Committed native memory- Events:
NativeMemoryUsage - Design: Table view, grouped by memory type, displays first observed value, average, last observed value, maximum
- Events:
native-memory-reserved: Reserved native memory- Events:
NativeMemoryUsage - Design: Table view, grouped by memory type, displays first observed value, average, last observed value, maximum
- Events:
tlabs: Thread local allocation buffers- Events:
ObjectAllocationInNewTLAB,ObjectAllocationOutsideTLAB - Design: Form view, displays allocation statistics inside/outside TLAB (count, min/avg/max size, total allocation)
- Events:
Application Views (application.*):
hot-methods: Hot methods- Events:
ExecutionSample - Design: Table view, grouped by top stack frame, displays method, sample count, percentage, limited to top 25
- Aggregation: Uses
COUNT(*)for counting,normalizedformat displays percentage - Stack: Uses
stackTrace.topFrameto get outermost method
- Events:
cpu-time-hot-methods: CPU time sampling hot methods- Events:
CPUTimeSample - Design: Table view, grouped by top stack frame, displays method, sample count, percentage, limited to top 25
- Aggregation: Uses
COUNT(*)for counting,normalizedformat displays percentage
- Events:
cpu-time-statistics: CPU time sampling statistics- Events:
CPUTimeSample,CPUTimeSamplesLost - Design: Form view, displays successful samples, failed samples, biased samples, total samples, lost sample count
- Filter: Uses
WHEREconditions to filter samples of different states
- Events:
allocation-by-class: Allocation statistics by class- Events:
ObjectAllocationSample - Design: Table view, grouped by object class, displays object type, allocation pressure, limited to top 25
- Aggregation: Uses
SUM(weight)to calculate allocation pressure,normalizedformat displays
- Events:
allocation-by-thread: Allocation statistics by thread- Events:
ObjectAllocationSample - Design: Table view, grouped by event thread, displays thread, allocation pressure, limited to top 25
- Aggregation: Uses
SUM(weight)to calculate allocation pressure
- Events:
allocation-by-site: Allocation statistics by site- Events:
ObjectAllocationSample - Design: Table view, grouped by top stack frame, displays method, allocation pressure, limited to top 25
- Stack: Uses
stackTrace.topFrameto get allocation location
- Events:
contention-by-thread: Lock contention statistics by thread- Events:
JavaMonitorEnter - Design: Table view, grouped by event thread, displays thread, count, average, P90, max duration
- Events:
contention-by-class: Lock contention statistics by lock class- Events:
JavaMonitorEnter - Design: Table view, grouped by monitor class, displays lock class, count, average, P90, max duration
- Events:
contention-by-site: Lock contention statistics by site- Events:
JavaMonitorEnter - Design: Table view, grouped by stack trace, displays stack trace, count, average, max duration
- Events:
contention-by-address: Lock contention statistics by monitor address- Events:
JavaMonitorEnter - Design: Table view, grouped by monitor class, displays address, class, thread count, max duration
- Aggregation: Uses
FIRST()to get first class name,UNIQUE()to get unique thread count
- Events:
memory-leaks-by-class: Memory leak candidates by class- Events:
OldObjectSample - Design: Table view, grouped by object type, displays allocation time, object class, object age, heap usage
- Aggregation: Uses
LAST_BATCH()to get last batch of values - Note: Using
memory-leaksview automatically triggers OldObjectSample event collection
- Events:
memory-leaks-by-site: Memory leak candidates by site- Events:
OldObjectSample - Design: Table view, grouped by application method, displays allocation time, application method, object age, heap usage
- Stack: Uses
stackTrace.topApplicationFrameto get application layer allocation location
- Events:
exception-by-type: Exception statistics by type- Events:
JavaErrorThrow,JavaExceptionThrow - Design: Table view, grouped by throw class, displays class, count
- Events:
exception-by-message: Exception statistics by message- Events:
JavaErrorThrow,JavaExceptionThrow - Design: Table view, grouped by message, displays message, count
- Events:
exception-by-site: Exception statistics by site- Events:
JavaErrorThrow,JavaExceptionThrow - Design: Table view, grouped by top non-initialization stack frame, displays method, count
- Stack: Uses
stackTrace.topNotInitFrameto exclude<init>methods
- Events:
exception-count: Exception statistics- Events:
ExceptionStatistics - Design: Form view, displays total thrown exceptions (uses
DIFF(throwables)to calculate difference)
- Events:
thread-allocation: Thread allocation statistics- Events:
ThreadAllocationStatistics - Design: Table view, grouped by thread, displays thread, allocated, percentage
- Aggregation: Uses
LAST()to get latest value,normalizedformat displays percentage
- Events:
thread-cpu-load: Thread CPU load- Events:
ThreadCPULoad - Design: Table view, grouped by event thread, displays thread, system CPU, user CPU
- Events:
thread-start: Platform thread start- Events:
ThreadStart,ThreadEnd - Design: Table view, grouped by event thread, displays start time, stack trace, thread, duration
- Aggregation: Uses
DIFF(startTime)to calculate thread lifecycle
- Events:
thread-count: Java thread statistics- Events:
JavaThreadStatistics - Design: Table view, displays all fields (
SELECT *)
- Events:
pinned-threads: Pinned virtual threads- Events:
VirtualThreadPinned - Design: Table view, grouped by application method, displays method, pin count, longest pin time, total pin time
- Stack: Uses
stackTrace.topApplicationFrameto get application layer method
- Events:
file-reads-by-path: File read statistics by path- Events:
FileRead - Design: Table view, grouped by path, displays path, read count, total read bytes
- Format:
cell-height:5supports multi-line path display
- Events:
file-writes-by-path: File write statistics by path- Events:
FileWrite - Design: Table view, grouped by path, displays path, write count, total write bytes
- Events:
socket-reads-by-host: Socket read statistics by host- Events:
SocketRead - Design: Table view, grouped by host, displays host, read count, total read bytes
- Events:
socket-writes-by-host: Socket write statistics by host- Events:
SocketWrite - Design: Table view, grouped by host, displays host, write count, total write bytes
- Events:
latencies-by-type: Latency statistics by type- Events:
JavaMonitorWait,JavaMonitorEnter,ThreadPark,ThreadSleep,SocketRead,SocketWrite,FileWrite,FileRead - Design: Table view, grouped by event type, displays event type, count, average, P99, longest, total duration
- Multiple events: Uses union of multiple event types
- Events:
object-statistics: Object statistics (occupying more than 1%)- Events:
ObjectCountAfterGC,ObjectCount - Design: Table view, grouped by object class, displays class, count, heap space, growth
- Aggregation: Uses
LAST_BATCH()to get last batch of values,DIFF()to calculate growth
- Events:
class-loaders: Class loaders- Events:
ClassLoaderStatistics - Design: Table view, grouped by class loader, displays class loader, hidden class count, class count
- Format:
missing:null-bootstrapdisplays bootstrap class loader as null
- Events:
longest-class-loading: Longest class loading- Events:
ClassLoad - Design: Table view, displays top 25 classes with longest loading time
- Events:
finalizers: Finalizers- Events:
FinalizerStatistics - Design: Table view, grouped by finalizable class, displays class, object count, total run count
- Aggregation: Uses
LAST_BATCH()to get last batch of values
- Events:
deprecated-methods-for-removal: Deprecated methods marked for removal- Events:
DeprecatedInvocation - Design: Table view, grouped by method, displays deprecated method, set of calling source classes
- Filter:
WHERE forRemoval = 'true' - Aggregation: Uses
SET()to get unique set of calling source classes - Format:
truncate-beginningandcell-height:10000support long content display
- Events:
method-timing: Method timing- Events:
jdk.MethodTiming - Design: Table view, grouped by method, displays method, call count, min/avg/max time
- Aggregation: Uses
LAST_BATCH()to get last batch of values - Format:
ms-precision:6displays microsecond precision
- Events:
method-calls: Method calls- Events:
jdk.MethodTrace - Design: Table view, grouped by traced method and caller, displays traced method, caller, call count
- Stack: Uses
stackTrace.topFrame.methodto get caller method
- Events:
monitor-inflation: Monitor inflation- Events:
jdk.JavaMonitorInflate - Design: Table view, grouped by stack trace and monitor class, displays stack trace, monitor class, count, total duration
- Events:
modules: Modules- Events:
ModuleRequire - Design: Table view, grouped by source module name, displays module name
- Events:
native-methods: Native methods- Events:
NativeMethodSample - Design: Table view, grouped by top stack frame, displays method, sample count
- Events:
Environment Views (environment.*):
recording: Recording information- Events: All events (
FROM *) - Design: Form view, displays event count, first/last recorded event, recording length, dump reason
- Aggregation: Uses
COUNT(),FIRST(),LAST(),DIFF()functions, gets dump reason fromjdk.Shutdown
- Events: All events (
active-recordings: Active recordings- Events:
ActiveRecording - Design: Table view, grouped by recording ID, displays start time, duration, name, destination, max age, max size
- Format:
cell-height:5supports multi-line destination path display
- Events:
active-settings: Active settings- Events:
ActiveSetting - Design: Table view, grouped by event type ID, displays event type, enabled state, threshold, stack trace, period, cutoff, throttle
- Multiple sources: Uses multiple
ActiveSettingaliases (E, T, S, P, C, U) to get different settings respectively - Filter: Uses
WHEREconditions to filter different setting names
- Events:
cpu-load: CPU load statistics- Events:
CPULoad - Design: Form view, displays min/avg/max of JVM user/system CPU and machine total CPU
- Events:
cpu-load-samples: CPU load samples- Events:
CPULoad - Design: Table view, displays start time, JVM user/system CPU, machine total CPU for each sample
- Events:
cpu-information: CPU information- Events:
CPUInformation - Design: Form view, displays CPU, socket count, core count, hardware thread count, description
- Events:
cpu-tsc: CPU timestamp counter- Events:
CPUTimeStampCounter - Design: Form view, displays fast time auto-enabled, fast time enabled, fast time frequency, OS frequency
- Events:
system-information: System information- Events:
CPUInformation,PhysicalMemory,OSInformation,VirtualizationInformation - Design: Form view, displays physical memory size, OS version, virtualization, CPU type, core count, hardware thread count, socket count, CPU description
- Multiple events: Uses union of multiple event types
- Events:
system-properties: System properties- Events:
InitialSystemProperty - Design: Table view, grouped by key, displays key, value
- Format:
cell-height:25supports long property value display
- Events:
system-processes: System processes- Events:
SystemProcess - Design: Table view, grouped by PID, displays first/last observed time, PID, command line
- Format:
truncate-beginningtruncates long command lines
- Events:
environment-variables: Environment variables- Events:
InitialEnvironmentVariable - Design: Table view, grouped by key, displays key, value
- Format:
cell-height:20supports long environment variable value display
- Events:
network-utilization: Network utilization- Events:
NetworkUtilization - Design: Table view, grouped by network interface, displays interface, avg/max read rate, avg/max write rate
- Events:
native-libraries: Native libraries- Events:
NativeLibrary - Design: Table view, grouped by name, displays name, base address, top address
- Events:
native-library-failures: Native library load/unload failures- Events:
NativeLibraryLoad,NativeLibraryUnload - Design: Table view, filters failed operations, displays operation type, library name, error message
- Filter:
WHERE success = 'false'
- Events:
jvm-flags: JVM flags- Events:
IntFlag,UnsignedIntFlag,BooleanFlag,LongFlag,UnsignedLongFlag,DoubleFlag,StringFlagand their change events - Design: Table view, grouped by name, displays name, last value
- Multiple events: Uses union of multiple flag event types
- Events:
jvm-information: JVM information- Events:
JVMInformation - Design: Form view, displays PID, VM start time, name, version, VM arguments, program arguments
- Events:
jdk-agents: JDK agents- Events:
JavaAgent,NativeAgent - Design: Table view, displays initialization time, initialization duration, name, options
- Format:
truncate-beginningandcell-height:10support long options display
- Events:
container-configuration: Container configuration- Events:
ContainerConfiguration - Design: Form view, displays container type, CPU slice period, CPU quota, CPU shares, effective CPU count, memory soft limit, memory limit, swap memory limit, host total memory
- Events:
container-cpu-usage: Container CPU usage- Events:
ContainerCPUUsage - Design: Form view, displays CPU time, user time, system time
- Events:
container-memory-usage: Container memory usage- Events:
ContainerMemoryUsage - Design: Form view, displays memory failure count, memory usage, swap memory usage
- Events:
container-io-usage: Container I/O usage- Events:
ContainerIOUsage - Design: Form view, displays service request count, data transfer amount
- Events:
container-cpu-throttling: Container CPU throttling- Events:
ContainerCPUThrottling - Design: Form view, displays CPU used slices, CPU throttled slices, CPU throttled time
- Events:
events-by-count: Event types by count- Events: All events (
FROM *) - Design: Table view, grouped by event type label, displays event type, count, sorted by count descending
- Events: All events (
events-by-name: Event types by name- Events: All events (
FROM *) - Design: Table view, grouped by event type label, displays event type, count, sorted by name ascending
- Events: All events (
View design characteristics:
- Categorized organization: Views are organized by function (JVM, application, environment), easy to find and use
- Query optimization: Uses
LIMITto limit result count, avoid outputting too much data - Formatting options: Uses
FORMATto control display format, improve readability - Multiple event support: Supports querying data from multiple event types (using union)
- Stack trace access: Provides multiple stack frame access methods, adapt to different analysis scenarios
- Rich aggregation functions: Supports statistics, percentiles, sets and other aggregation functions
- Real-time query: Directly reads data from repository, no need to dump file
Notes:
- Real-time viewing:
JFR.viewdirectly reads data from repository, no need to dump file - Memory leak view: When using
memory-leaksview, automatically triggers OldObjectSample event collection (callsOldObjectSample.emit(0)and waits for flush) - Time range: Default views data from last 10 minutes, can be adjusted via
maxage - Size limit: Default views at most 32MB of data, can be adjusted via
maxsize - Query execution: View queries are executed via
QueryExecutor, usesEventStreamto read event data - Performance consideration: Complex queries (such as
all-views) may consume more time and memory - Missing events: If recording lacks events required by view, will display error message but will not interrupt execution
- View name format: Views are defined in
view.iniusing full names (such as[jvm.gc],[application.hot-methods]), but inJFR.viewcommand only need to use view name part (such asgc,hot-methods), no need to add category prefix (jvm.,application.,environment.). View name matching is case-insensitive.
Custom queries and query language:
JFR.view command can only use predefined views, does not support directly executing SQL queries.
Query language usage scenarios:
- Predefined views (
JFR.view): Uses views predefined inview.ini, called via view name - View definition (
view.ini): Defines views in configuration file, forJFR.viewto use - Custom queries (
jfr query): For dumped JFR files, usesjfr querycommand to directly write query statements (Note:jfr querycommand is only available in debug builds, for OpenJDK developers, may be removed or syntax changed)
Notes on custom queries:
jcmd JFR.querycommand does not exist: AlthoughDCmdQuery.javaimplementation exists in source code, the command registration injfrDcmds.cppis commented out (// JFR.query Uncomment when developing new queries for the JFR.view command), therefore in actual JDK builds cannot usejcmd JFR.querycommand.jfr querycommand: If custom queries need to be executed, can usejfr querycommand (for dumped JFR files), this command supports same query language syntax as inview.ini. But note:jfr querycommand is only available in debug builds- This tool is for OpenJDK developers, may be removed or syntax changed
- Usage:
jfr query "<query>" <file.jfr>
Can use verbose=true option to view query statements of predefined views, then copy to jfr query for use or modification:
# View query statements of predefined views
jcmd <pid> JFR.view verbose=true gc
# Use custom query (jfr query) - for dumped JFR files (only available in debug builds)
jfr query "SELECT * FROM GarbageCollection" recording.jfr
jfr query "SELECT stackTrace.topFrame AS T, SUM(weight) FROM ObjectAllocationSample GROUP BY T" recording.jfr
4.7. Command Usage Flow Example#
The following is a complete usage flow example:
# 1. View currently running recordings
jcmd <pid> JFR.check
# 2. Start a new recording
jcmd <pid> JFR.start name=PerformanceAnalysis settings=profile duration=30m filename=analysis.jfr
# 3. View recording status
jcmd <pid> JFR.check name=PerformanceAnalysis
# 4. Dump data during recording (recording continues running)
jcmd <pid> JFR.dump name=PerformanceAnalysis maxage=10m filename=snapshot.jfr
# 5. View real-time views
jcmd <pid> JFR.view hot-methods
jcmd <pid> JFR.view gc
# 6. Stop recording and dump
jcmd <pid> JFR.stop name=PerformanceAnalysis filename=final.jfr
# 7. View all recordings again
jcmd <pid> JFR.check
5. JFC Configuration File Format and Configuration Usage#
JFC (Java Flight Recorder Configuration) files are XML format configuration files used to define events to be collected in JFR recordings and their configuration parameters. JFC files are the core configuration mechanism of JFR, through which you can precisely control which events are recorded, event collection frequency, thresholds, etc.
5.1. JFC File Format#
JFC files use XML format and must conform to jfc.xsd Schema definition. Basic file structure is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Configuration Name" description="Configuration Description" provider="Provider">
<event name="Event Name">
<setting name="Configuration Item Name">Configuration Value</setting>
<setting name="Configuration Item Name" control="Control Identifier">Configuration Value</setting>
</event>
<!-- More event configurations -->
</configuration>
Root element <configuration> attributes:
version(required): JFC file format version, current version is"2.0". JFR can only read JFC files of version 2.xlabel(required): Display name of configuration file, used to identify this configuration in toolsdescription(optional): Description information of configuration fileprovider(optional): Provider of configuration file, such as"Oracle","OpenJDK", etc.
<event> element:
name(required): Event name, such as"jdk.ThreadStart","jdk.GCPhase", etc.label(optional): Display label of eventdescription(optional): Description information of event
<setting> element:
name(required): Configuration item name, such as"enabled","threshold","period","stackTrace","throttle", etc.control(optional): Control identifier, used to associate with controls defined in<control>element. When control value changes, associated setting value automatically updates- Element content: Configuration value, such as
"true","20 ms","100/s", etc.
<control> element (optional, JVM does not read):
- Location: Under
<configuration>root element, same level as<event>elements - Function: Defines UI controls and conditional logic, for JDK Mission Control and
jfr configuretools - Contains child elements:
<text>: Text input control<selection>: Selection control (dropdown menu)<flag>: Boolean flag control<condition>: Conditional logic, dynamically sets configuration values based on other control values
Example:
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Custom" description="Custom JFR configuration" provider="User">
<!-- Enable thread sleep event, only record sleeps exceeding 20ms, and collect stack traces -->
<event name="jdk.ThreadSleep">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold">20 ms</setting>
</event>
<!-- Enable CPU load event, collect once per second -->
<event name="jdk.CPULoad">
<setting name="enabled">true</setting>
<setting name="period">1 s</setting>
</event>
<!-- Disable class loading statistics event -->
<event name="jdk.ClassLoadingStatistics">
<setting name="enabled">false</setting>
</event>
</configuration>
5.2. Control Element Details#
<control> element is a special part in JFC files, used to define UI controls and conditional logic. Important: JVM does not read <control> element content at runtime, it is only for JDK Mission Control (JMC) and jfr configure tools, providing friendly configuration interface and automated configuration logic.
5.2.1. Control Mechanism#
- UI control definition: Provides configuration interface elements for JMC and
jfr configuretools - Configuration association: Associates event configurations with controls via
controlattribute of<setting>elements - Automatic update: When control value changes, all associated setting values automatically update
- Conditional logic: Implements dynamic configuration based on other control values via
<condition>elements
5.2.2. Control Element Types#
1. <text> Element (Text Input Control)
Used to define text input controls, supports different content types:
<text name="Control Name" label="Display Label" contentType="Content Type" minimum="Minimum Value" maximum="Maximum Value">Default Value</text>
Attributes:
name(required): Control identifier, used to reference incontrolattribute of<setting>label(required): Display label of controldescription(optional): Control descriptioncontentType(optional): Content type, such as"timespan"(time interval),"method-filter"(method filter),"text"(plain text), etc.minimum(optional): Minimum value (for sortable content types)maximum(optional): Maximum value (for sortable content types)
Example:
<!-- Define lock wait threshold control -->
<text name="locking-threshold" label="Locking Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<!-- Define method filter control -->
<text name="method-trace" label="Method Trace Filter" contentType="method-filter"
description="A filter can be an annotation (@jakarta.ws.rs.GET), a full qualified class name (com.example.Foo), a fully qualified method reference (java.lang.HashMap::resize) or a class initializer (::<clinit>). Use <init> for constructors. Separate multiple filters with semicolon."></text>
Usage:
<!-- Associate control in event configuration -->
<event name="jdk.ThreadSleep">
<setting name="threshold" control="locking-threshold">20 ms</setting>
</event>
<event name="jdk.JavaMonitorEnter">
<setting name="threshold" control="locking-threshold">20 ms</setting>
</event>
When user modifies locking-threshold control value via JMC or jfr configure, all associated setting values automatically update.
2. <selection> Element (Selection Control)
Used to define dropdown selection controls, provides multiple predefined options:
<selection name="Control Name" label="Display Label" default="Default Option Name">
<option label="Option Display Label" name="Option Name">Option Value</option>
<!-- More options -->
</selection>
Attributes:
name(required): Control identifierlabel(required): Display label of controldescription(optional): Control descriptiondefault(required): Name of default option (must matchnameattribute of some<option>)
<option> element:
name(required): Option identifier, used to reference indefaultattributelabel(required): Display label of optiondescription(optional): Option description- Element content: Actual value of option
Example:
<!-- Define method profiling level selection control -->
<selection name="method-profiling" default="normal" label="Method Profiling">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="High" name="high">high</option>
<option label="Maximum (High Overhead)" name="max">max</option>
</selection>
<!-- Define GC event level selection control -->
<selection name="gc" default="normal" label="GC">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="Detailed" name="detailed">detailed</option>
<option label="High, incl. TLABs/PLABs (may cause many events)" name="high">high</option>
<option label="All, incl. Heap Statistics (may cause long GCs)" name="all">all</option>
</selection>
3. <flag> Element (Boolean Flag Control)
Used to define boolean type switch controls:
<flag name="Control Name" label="Display Label">true|false</flag>
Attributes:
name(required): Control identifierlabel(required): Display label of controldescription(optional): Control description- Element content: Default value (
"true"or"false")
Example:
<!-- Define class loading event switch -->
<flag name="class-loading" label="Class Loading">false</flag>
Usage:
<!-- Associate control in event configuration -->
<event name="jdk.ClassLoad">
<setting name="enabled" control="class-loading">false</setting>
</event>
<event name="jdk.ClassUnload">
<setting name="enabled" control="class-loading">false</setting>
</event>
4. <condition> Element (Conditional Logic)
Used to dynamically set configuration values based on other control values, implements conditional logic:
<condition name="Control Name" true="Value When Condition Is True" false="Value When Condition Is False">
<!-- Condition expression: <test>, <and>, <or>, <not> -->
</condition>
Attributes:
name(required): Control identifier, used to reference incontrolattribute of<setting>true(optional): Value returned when condition expression is truefalse(optional): Value returned when condition expression is false
Condition expression elements:
<test>: Test condition, checks value of some controlname: Name of control to testoperator: Operator, currently only supports"equal"value: Value to compare
<and>: Logical AND, returns true when all child conditions are true<or>: Logical OR, returns true when any child condition is true<not>: Logical NOT, inverts result of child condition
Example:
<!-- Dynamically set method sampling interval based on method-profiling selection control value -->
<condition name="method-sampling-java-interval" true="999 d">
<test name="method-profiling" operator="equal" value="off"/>
</condition>
<condition name="method-sampling-java-interval" true="20 ms">
<test name="method-profiling" operator="equal" value="normal"/>
</condition>
<condition name="method-sampling-java-interval" true="10 ms">
<test name="method-profiling" operator="equal" value="high"/>
</condition>
<condition name="method-sampling-java-interval" true="1 ms">
<test name="method-profiling" operator="equal" value="max"/>
</condition>
<!-- Dynamically enable/disable GC events based on GC selection control value -->
<condition name="gc-enabled-normal" true="true" false="false">
<or>
<test name="gc" operator="equal" value="normal"/>
<test name="gc" operator="equal" value="detailed"/>
<test name="gc" operator="equal" value="high"/>
<test name="gc" operator="equal" value="all"/>
</or>
</condition>
<!-- Dynamically enable/disable thread dump based on thread-dump selection control value -->
<condition name="thread-dump-enabled" true="false" false="true">
<test name="thread-dump" operator="equal" value="999 d"/>
</condition>
Usage:
<!-- Associate condition control in event configuration -->
<event name="jdk.ExecutionSample">
<setting name="enabled" control="method-sampling-enabled">true</setting>
<setting name="period" control="method-sampling-java-interval">20 ms</setting>
</event>
<event name="jdk.GCPhase">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
5.2.3. Complete Control Example#
The following is a complete control example, demonstrating combined use of various control types:
<configuration version="2.0" label="Custom" description="Custom JFR configuration">
<!-- Event configurations -->
<event name="jdk.ThreadSleep">
<setting name="enabled">true</setting>
<setting name="threshold" control="locking-threshold">20 ms</setting>
</event>
<event name="jdk.ExecutionSample">
<setting name="enabled" control="method-sampling-enabled">true</setting>
<setting name="period" control="method-sampling-java-interval">20 ms</setting>
</event>
<event name="jdk.GCPhase">
<setting name="enabled" control="gc-enabled-normal">true</setting>
</event>
<!-- Control section -->
<control>
<!-- Text control: Lock wait threshold -->
<text name="locking-threshold" label="Locking Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<!-- Selection control: Method profiling level -->
<selection name="method-profiling" default="normal" label="Method Profiling">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="High" name="high">high</option>
<option label="Maximum (High Overhead)" name="max">max</option>
</selection>
<!-- Condition control: Set sampling interval based on method profiling level -->
<condition name="method-sampling-java-interval" true="999 d">
<test name="method-profiling" operator="equal" value="off"/>
</condition>
<condition name="method-sampling-java-interval" true="20 ms">
<test name="method-profiling" operator="equal" value="normal"/>
</condition>
<condition name="method-sampling-java-interval" true="10 ms">
<test name="method-profiling" operator="equal" value="high"/>
</condition>
<condition name="method-sampling-java-interval" true="1 ms">
<test name="method-profiling" operator="equal" value="max"/>
</condition>
<!-- Condition control: Enable/disable sampling based on method profiling level -->
<condition name="method-sampling-enabled" true="false" false="true">
<test name="method-profiling" operator="equal" value="off"/>
</condition>
<!-- Selection control: GC event level -->
<selection name="gc" default="normal" label="GC">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="Detailed" name="detailed">detailed</option>
<option label="High" name="high">high</option>
<option label="All" name="all">all</option>
</selection>
<!-- Condition control: Enable/disable GC events based on GC level -->
<condition name="gc-enabled-normal" true="true" false="false">
<or>
<test name="gc" operator="equal" value="normal"/>
<test name="gc" operator="equal" value="detailed"/>
<test name="gc" operator="equal" value="high"/>
<test name="gc" operator="equal" value="all"/>
</or>
</condition>
</control>
</configuration>
5.2.4. How Control Works#
Based on source code analysis (JFCModel.java), control workflow is as follows:
Parsing phase (
addControls()):- Parse all
<control>elements and their child elements (<text>,<selection>,<flag>,<condition>) - Index controls by name into
controlsMap
- Parse all
Association phase (
wireSettings()):- Traverse all event settings
- If setting has
controlattribute, find corresponding control - Establish listener relationship: when control value changes, automatically update associated setting value
Condition calculation phase (
wireConditions()):- Parse expressions of all
<condition>elements - Establish dependency relationship: when tested control value changes, recalculate condition value
- After condition value is calculated, automatically update associated setting value
- Parse expressions of all
Runtime:
- JVM does not read control section, only reads final setting values
- Control is only for configuration tools (JMC,
jfr configure) to provide friendly configuration interface
5.2.5. Advantages of Control#
- Simplify configuration: Automatically set multiple related configurations through high-level controls (such as “Method Profiling” level)
- Consistency guarantee: Related event configurations are uniformly managed through same control, ensuring configuration consistency
- User friendly: Provides graphical configuration interface in JMC, no need to manually edit XML
- Conditional logic: Implements complex configuration dependencies through
<condition>
5.2.6. Notes#
- JVM does not read: Control elements have no impact on JVM runtime, JVM only reads final setting values
- Uniqueness requirement: When merging multiple JFC files, control names cannot be duplicated (will throw exception)
- Reference integrity: If setting’s
controlattribute references non-existent control, will log warning but will not error - Manual editing: If manually editing JFC files, need to ensure control names match setting’s
controlattributes
5.4. Predefined JFC Configuration Files#
JDK provides two predefined JFC configuration files, located in JAVA_HOME/lib/jfr/ directory:
default.jfc(default configuration):- Label:
"Continuous" - Description:
"Low overhead configuration safe for continuous use in production environments, typically less than 1 % overhead." - Characteristics: Low overhead configuration, suitable for continuous use in production environments, overhead typically less than 1%
- Applicable scenarios: Continuous monitoring in production environments
- Label:
profile.jfc(profiling configuration):- Label:
"Profiling" - Description:
"Low overhead configuration for profiling, typically around 2 % overhead." - Characteristics: Profiling configuration, overhead around 2%, includes more events and stack traces
- Applicable scenarios: Performance analysis and problem diagnosis
- Label:
View predefined configurations:
# View content of default.jfc
cat $JAVA_HOME/lib/jfr/default.jfc
# View content of profile.jfc
cat $JAVA_HOME/lib/jfr/profile.jfc
# List all .jfc files in JAVA_HOME/lib/jfr/ directory
ls $JAVA_HOME/lib/jfr/*.jfc
# View help information for jfr configure command (will show available input file options)
jfr help configure
Note: jfr configure command does not have --list option. To list all available predefined configurations, can use the following methods:
- Directly view directory:
JAVA_HOME/lib/jfr/directory usually containsdefault.jfcandprofile.jfc - Use JDK API: Get configuration list via
Configuration.getConfigurations()method (requires writing Java code) - View help information:
jfr help configurewill show available input file options
5.5. Creating and Editing JFC Configuration Files#
5.5.1. Creating Using jfr configure Command#
jfr configure command provides both interactive and command-line ways to create JFC files:
Interactive creation:
Interactive mode requires explicitly specifying --interactive parameter, will start configuration wizard, step by step asking configuration options. Wizard will traverse all input items defined by <control> elements in JFC file (<selection>, <text>, <flag>), asking user input one by one.
# Create custom configuration based on default.jfc (interactive)
jfr configure --interactive --input default.jfc --output custom.jfc
# Create custom configuration based on profile.jfc (interactive)
jfr configure --interactive --input profile.jfc --output custom.jfc
# Only use --interactive, do not specify --input (default uses default.jfc)
jfr configure --interactive
# Example interaction:
============== .jfc Configuration Wizard ============
This wizard will generate a JFR configuration file by
asking 12 questions. Press ENTER to use the default
value, or type Q to abort the wizard.
Garbage Collector: Normal (default)
1. Off
2. Normal
3. Detailed
4. High, incl. TLABs/PLABs (may cause many events)
5. All, incl. Heap Statistics (may cause long GCs)
How interactive mode works:
- Start wizard: Display welcome message, inform how many questions will be asked
- Traverse input items: For each input item in
<control>(<selection>,<text>,<flag>), ask in turn:<selection>: Display option list (1, 2, 3…), user inputs number to select, or press ENTER to use default value<text>: Display label and default value, user inputs text, or press ENTER to use default value<flag>: Display Y/N choice, user inputs Y or N, or press ENTER to use default value
- Input validation: For timespan and method-filter types, format validation is performed
- Exit method: Input
Qcan exit wizard at any time - Save file: Finally asks output filename (default
custom.jfc)
Command-line creation:
# Create based on default.jfc, and modify specific event configurations
jfr configure \
--input default.jfc \
--output custom.jfc \
jdk.ThreadSleep#threshold=50ms \
jdk.CPULoad#period=2s
# Use + prefix to add new event configuration (if event is not in base configuration)
jfr configure \
--input default.jfc \
--output custom.jfc \
+jdk.CustomEvent#enabled=true
# Note: Cannot merge JFC files containing same control names
# For example default.jfc and profile.jfc both define 'gc' control, cannot directly merge
# If attempt to merge will error: Control with 'gc' is declared in multiple files
# Correct approach: Only use one base JFC file, then override configuration via command-line parameters
jfr configure \
--input default.jfc \
--output custom.jfc \
gc=high \
method-profiling=high
# Or use profile.jfc as base
jfr configure \
--input profile.jfc \
--output custom.jfc \
gc=high
Configuration option format:
- Option format:
<option name>=<value>(such asgc=high,method-profiling=high) - Event setting format:
<event name>#<configuration item name>=<configuration value>(such asjdk.ThreadSleep#threshold=50ms,jdk.CPULoad#period=2s) - Add new event: Use
+prefix, such as+jdk.CustomEvent#enabled=true - Time span format: Supports
20ms,1s,5mand other formats, spaces can be omitted
Notes:
- Prerequisite for interactive mode: JFC file must contain input items defined by
<control>elements, otherwise interactive mode will not ask any questions - Default JFC file: If
--inputis not specified, default usesdefault.jfc - Empty configuration: Can use
--input noneto start creating from empty configuration - Display modifications: Use
--verboseparameter can display all modified settings - Limitations of merging multiple JFC files:
- Limitation of
jfr configurecommand: Cannot merge JFC files containing same control names. For example,default.jfcandprofile.jfcboth definegc,method-profilingand other controls, attempting to usejfr configure --input default.jfc,profile.jfcwill error:Control with 'gc' is declared in multiple files - Correct approach: Only use one base JFC file (such as
default.jfcorprofile.jfc), then override configuration via command-line parameters - Difference between JVM parameters and jcmd commands: Although
jfr configurecannot merge JFC files with conflicts, can merge via JVM parameters (-XX:StartFlightRecording=settings=default,settings=profile) orjcmdcommands (settings=default,settings=profile), because JVM runtime only reads final setting values, does not care about control elements
- Limitation of
5.5.2. Manually Editing JFC Files#
Can directly edit XML files, but need to note:
- Must conform to
jfc.xsdSchema - Recommend using editor that supports XML Schema validation
- After modification can use
jfr configure --input <file>to validate format
Validate JFC file:
# Method 1: Try to load and output to temporary file (delete after validation)
# Note: Output file must end with .jfc, cannot use /dev/null
jfr configure --input custom.jfc --output /tmp/validate.jfc && rm /tmp/validate.jfc
# Method 2: Output to temporary file in current directory (delete after validation)
jfr configure --input custom.jfc --output validate-temp.jfc && rm validate-temp.jfc
Validation principle:
jfr configurecommand performs format validation when parsing and saving JFC files- If JFC file format is incorrect (such as XML format error, does not conform to Schema, control reference error, etc.), will throw exception during parsing or saving phase
- Output file must be valid file path ending with
.jfc, cannot use special device files like/dev/null
5.6. Using JFC Configuration Files#
5.6.1. Using via JVM Parameters#
Specify single JFC file:
# Use predefined configuration (default.jfc)
-XX:StartFlightRecording=settings=default
# Use predefined configuration (profile.jfc)
-XX:StartFlightRecording=settings=profile
# Use custom JFC file (full path)
-XX:StartFlightRecording=settings=/path/to/custom.jfc
# Use custom JFC file (relative path, relative to current working directory)
-XX:StartFlightRecording=settings=./custom.jfc
# Do not use any predefined configuration (start from blank configuration)
-XX:StartFlightRecording=settings=none
Specify multiple JFC files (merge configurations):
# Merge multiple JFC files, later configurations will override earlier configurations with same name
# Note: Can only merge JFC files that do not contain same control names
-XX:StartFlightRecording=settings=default,settings=profile
# Merge custom configuration and predefined configuration
-XX:StartFlightRecording=settings=default,settings=/path/to/custom.jfc
Merge rules:
- Multiple JFC files are loaded and merged in order
- Configurations in later files will override configurations with same name in earlier files
- If multiple files define same configuration item for same event, value from last file takes effect
- Important limitation: Control elements cannot be redefined. If multiple files define same control name (such as
gc,method-profiling, etc.),jfr configurecommand will throw exception:Control with 'gc' is declared in multiple files - Actual impact: Since
default.jfcandprofile.jfcboth define same control names (such asgc,method-profiling, etc.), they cannot be merged viajfr configure --input default.jfc,profile.jfc. But can be merged via JVM parameters orjcmdcommand’ssettings=default,settings=profilemethod, because JVM at runtime only reads final setting values, does not care about control elements
Specify JFC file and override configuration simultaneously:
# Use default.jfc, and override specific event configurations
-XX:StartFlightRecording=settings=default,jdk.ThreadSleep#threshold=50ms,jdk.CPULoad#period=2s
5.6.2. Using via jcmd Commands#
# Start recording, use default.jfc
jcmd <pid> JFR.start settings=default
# Start recording, use custom JFC file
jcmd <pid> JFR.start settings=/path/to/custom.jfc
# Start recording, use multiple JFC files
# Note: Although default.jfc and profile.jfc cannot be merged via jfr configure (because control name conflicts),
# can be merged via JVM parameters or jcmd commands, because JVM runtime only reads final setting values
jcmd <pid> JFR.start settings=default,settings=profile
# Start recording, use JFC file and override configuration
jcmd <pid> JFR.start settings=default,jdk.ThreadSleep#threshold=50ms
5.6.3. Using via JDK API#
import jdk.jfr.Configuration;
import jdk.jfr.Recording;
// Load predefined configuration
Configuration config = Configuration.getConfiguration("default");
// Load configuration from file
Configuration config = Configuration.create(Path.of("/path/to/custom.jfc"));
// Load configuration from Reader
Configuration config = Configuration.create(new FileReader("/path/to/custom.jfc"));
// Create recording using configuration
Recording recording = new Recording(config);
recording.start();
5.6.4. Using via JMX#
import javax.management.MBeanServer;
import com.sun.management.HotSpotDiagnosticMXBean;
// Get FlightRecorderMXBean
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
FlightRecorderMXBean frBean = ManagementFactory.newPlatformMXBeanProxy(
server,
"jdk.management.jfr:type=FlightRecorder",
FlightRecorderMXBean.class
);
// Start recording, use default.jfc
long recordingId = frBean.newRecording();
frBean.setConfiguration(recordingId, "default");
frBean.startRecording(recordingId);
5.7. JFC File Search Path#
When specifying JFC file, JFR searches in the following order:
- Predefined configuration name: If name is
"default"or"profile", load fromJAVA_HOME/lib/jfr/directory - JAVA_HOME/lib/jfr/ directory: If file is in
JAVA_HOME/lib/jfr/directory, can directly use filename (no need for full path) - Full path: If full path is provided, directly use that path
- Relative path: If relative path is provided, relative to current working directory
Examples:
# Following methods are equivalent (if custom.jfc is in JAVA_HOME/lib/jfr/ directory)
-XX:StartFlightRecording=settings=custom
-XX:StartFlightRecording=settings=custom.jfc
-XX:StartFlightRecording=settings=$JAVA_HOME/lib/jfr/custom.jfc
# Use full path
-XX:StartFlightRecording=settings=/home/user/custom.jfc
# Use relative path (relative to current working directory)
-XX:StartFlightRecording=settings=./config/custom.jfc
5.8. Common Configuration Examples#
5.8.1. Low Overhead Production Configuration#
Based on default.jfc, disable high overhead events:
<configuration version="2.0" label="Production" description="Low overhead production configuration">
<!-- Disable method execution sampling (high overhead) -->
<event name="jdk.ExecutionSample">
<setting name="enabled">false</setting>
</event>
<!-- Disable object allocation sampling (high overhead) -->
<event name="jdk.ObjectAllocationSample">
<setting name="enabled">false</setting>
</event>
<!-- Enable critical events, but increase threshold to reduce event count -->
<event name="jdk.ThreadSleep">
<setting name="enabled">true</setting>
<setting name="threshold">100 ms</setting>
<setting name="stackTrace">false</setting>
</event>
</configuration>
5.8.2. Performance Profiling Configuration#
Based on profile.jfc, enable more events and stack traces:
<configuration version="2.0" label="Profiling" description="Performance profiling configuration">
<!-- Enable method execution sampling, reduce sampling frequency -->
<event name="jdk.ExecutionSample">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="throttle">50/s</setting>
</event>
<!-- Enable object allocation sampling -->
<event name="jdk.ObjectAllocationSample">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="throttle">100/s</setting>
</event>
<!-- Reduce lock wait threshold to capture more lock contention -->
<event name="jdk.JavaMonitorEnter">
<setting name="enabled">true</setting>
<setting name="threshold">1 ms</setting>
<setting name="stackTrace">true</setting>
</event>
</configuration>
5.8.3. Memory Leak Analysis Configuration#
Enable OldObjectSample events and configure GC root paths:
<configuration version="2.0" label="Memory Leak Analysis" description="Configuration for memory leak analysis">
<!-- Enable old object sampling -->
<event name="jdk.OldObjectSample">
<setting name="enabled">true</setting>
<setting name="cutoff">infinity</setting>
</event>
<!-- Enable GC related events -->
<event name="jdk.GCPhase">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
</event>
</configuration>
Usage:
# Specify path-to-gc-roots=true when starting recording (in recording level configuration)
-XX:StartFlightRecording=settings=memory-leak.jfc,path-to-gc-roots=true
5.9. Best Practices#
Production environment:
- Use
default.jfcor low overhead configuration based on it - Disable high overhead events (such as
ExecutionSample,ObjectAllocationSample) - Increase event thresholds to reduce event count
- Disable unnecessary stack traces
- Use
Problem diagnosis:
- Use
profile.jfcor configuration based on it - Enable relevant events and stack traces
- Adjust sampling frequency and thresholds as needed
- Use
Configuration management:
- Include custom JFC files in version control
- Create different configuration files for different scenarios
- Use
jfr configurecommand to create and modify configurations, avoid manually editing XML
Configuration validation:
- Use
jfr configure --input <file>to validate configuration file format - Validate configuration effectiveness in test environment before using in production
- Use
6. Using JFR via JDK API#
JDK provides complete JFR API, allowing direct control of JFR recordings in applications, creating custom events, reading and analyzing JFR data. JFR API is located in jdk.jfr package, mainly includes the following core classes:
jdk.jfr.Recording: Used to create, configure, start and stop JFR recordingsjdk.jfr.FlightRecorder: Used to access Flight Recorder instance and manage recordingsjdk.jfr.Configuration: Used to load and manage JFC configuration filesjdk.jfr.Event: Used to define custom eventsjdk.jfr.consumer.*: Used to read and analyze JFR data
6.1. Basic API Classes#
6.1.1. FlightRecorder Class#
FlightRecorder is the entry point to access Flight Recorder instance.
Main methods:
static FlightRecorder getFlightRecorder(): Get Flight Recorder instanceList<Recording> getRecordings(): Get all running recordingsRecording takeSnapshot(): Create snapshot of all recording datastatic void register(Class<? extends Event> eventClass): Register custom event classstatic void unregister(Class<? extends Event> eventClass): Unregister event classstatic boolean isAvailable(): Check if JFR is availablestatic boolean isInitialized(): Check if JFR is initialized
Usage example:
import jdk.jfr.FlightRecorder;
import jdk.jfr.Recording;
// Check if JFR is available
if (!FlightRecorder.isAvailable()) {
System.out.println("JFR is not available");
return;
}
// Get Flight Recorder instance
FlightRecorder recorder = FlightRecorder.getFlightRecorder();
// Get all recordings
List<Recording> recordings = recorder.getRecordings();
System.out.println("Active recordings: " + recordings.size());
// Create snapshot
Recording snapshot = recorder.takeSnapshot();
6.1.2. Recording Class#
Recording class is used to create and manage JFR recordings.
Recording states (RecordingState enum):
NEW: Initial state, recording created but not startedDELAYED: Scheduled delayed startRUNNING: RunningSTOPPED: Stopped, data still availableCLOSED: Closed, resources released
Main methods:
void start(): Start recordingvoid scheduleStart(Duration delay): Delay startboolean stop(): Stop recordingvoid close(): Close recording and release resourcesvoid dump(Path destination): Dump recording data to fileRecording copy(boolean stop): Create copy of recordingvoid setSettings(Map<String, String> settings): Set event configurationMap<String, String> getSettings(): Get current configurationvoid setName(String name): Set recording namevoid setDestination(Path destination): Set destination path to dump when stoppingvoid setMaxAge(Duration maxAge): Set maximum retention timevoid setMaxSize(long maxSize): Set maximum sizeRecordingState getState(): Get recording state
6.2. Creating and Starting Recordings#
6.2.1. Create Recording with Default Configuration#
import jdk.jfr.Recording;
import java.nio.file.Path;
import java.nio.file.Paths;
// Create recording (using default configuration)
Recording recording = new Recording();
// Set recording name
recording.setName("My Recording");
// Start recording
recording.start();
// ... Application runs ...
// Stop recording
recording.stop();
// Dump to file
Path destination = Paths.get("recording.jfr");
recording.dump(destination);
// Close recording
recording.close();
6.2.2. Create Recording with Predefined Configuration#
import jdk.jfr.Configuration;
import jdk.jfr.Recording;
import java.io.IOException;
import java.text.ParseException;
try {
// Load predefined configuration
Configuration config = Configuration.getConfiguration("default");
// Create recording using configuration
Recording recording = new Recording(config);
recording.setName("Default Configuration Recording");
recording.start();
// ... Application runs ...
recording.stop();
recording.dump(Paths.get("recording.jfr"));
recording.close();
} catch (IOException | ParseException e) {
e.printStackTrace();
}
6.2.3. Create Recording with Custom Configuration#
import jdk.jfr.Configuration;
import jdk.jfr.Recording;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
import java.text.ParseException;
try {
// Load configuration from file
Path configPath = Paths.get("custom.jfc");
Configuration config = Configuration.create(configPath);
// Create recording using configuration
Recording recording = new Recording(config);
recording.setName("Custom Configuration Recording");
recording.start();
// ... Application runs ...
recording.stop();
recording.dump(Paths.get("recording.jfr"));
recording.close();
} catch (IOException | ParseException e) {
e.printStackTrace();
}
6.2.4. Create Recording with Map Configuration#
import jdk.jfr.Recording;
import java.util.HashMap;
import java.util.Map;
// Create configuration Map
Map<String, String> settings = new HashMap<>();
settings.put("jdk.ThreadSleep#enabled", "true");
settings.put("jdk.ThreadSleep#threshold", "20 ms");
settings.put("jdk.CPULoad#enabled", "true");
settings.put("jdk.CPULoad#period", "1 s");
// Create recording using configuration
Recording recording = new Recording(settings);
recording.setName("Custom Settings Recording");
recording.start();
// ... Application runs ...
recording.stop();
recording.dump(Paths.get("recording.jfr"));
recording.close();
6.3. Configuring Recordings#
6.3.1. Configure Events Using EventSettings API#
import jdk.jfr.Recording;
import java.time.Duration;
Recording recording = new Recording();
// Enable event and configure
recording.enable("jdk.ThreadSleep")
.withThreshold(Duration.ofMillis(20))
.withStackTrace();
recording.enable("jdk.CPULoad")
.withPeriod(Duration.ofSeconds(1));
// Disable event
recording.disable("jdk.ExecutionSample");
recording.start();
// ... Application runs ...
recording.stop();
recording.dump(Paths.get("recording.jfr"));
recording.close();
6.3.2. Configure Events Using Map#
import jdk.jfr.Recording;
import java.util.HashMap;
import java.util.Map;
Recording recording = new Recording();
// Get current configuration
Map<String, String> settings = recording.getSettings();
// Add or modify configuration
settings.put("jdk.ThreadSleep#enabled", "true");
settings.put("jdk.ThreadSleep#threshold", "20 ms");
settings.put("jdk.ThreadSleep#stackTrace", "true");
settings.put("jdk.CPULoad#enabled", "true");
settings.put("jdk.CPULoad#period", "1 s");
// Apply configuration
recording.setSettings(settings);
recording.start();
// ... Application runs ...
recording.stop();
recording.dump(Paths.get("recording.jfr"));
recording.close();
6.3.3. Configure Recording Options#
import jdk.jfr.Recording;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
Recording recording = new Recording();
// Set recording name
recording.setName("Production Monitoring");
// Set maximum retention time (2 days)
recording.setMaxAge(Duration.ofDays(2));
// Set maximum size (500MB)
recording.setMaxSize(500 * 1024 * 1024);
// Set destination path to dump when stopping
recording.setDestination(Paths.get("production-recording.jfr"));
// Start recording
recording.start();
// ... Application runs ...
// Stop recording (will automatically dump to destination)
recording.stop();
recording.close();
6.4. Delayed Start and Auto-Stop#
import jdk.jfr.Recording;
import java.time.Duration;
Recording recording = new Recording();
recording.setName("Delayed Recording");
// Delay start by 5 minutes
recording.scheduleStart(Duration.ofMinutes(5));
// Or start immediately, but set auto-stop time
recording.start();
// Note: Recording class itself does not provide auto-stop functionality
// Need to call stop() after waiting specified time in separate thread
// Auto-stop in separate thread
new Thread(() -> {
try {
Thread.sleep(Duration.ofMinutes(30).toMillis());
recording.stop();
recording.dump(Paths.get("recording.jfr"));
recording.close();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
6.5. Creating Recording Snapshots#
import jdk.jfr.FlightRecorder;
import jdk.jfr.Recording;
import java.nio.file.Paths;
FlightRecorder recorder = FlightRecorder.getFlightRecorder();
// Create snapshot of all recording data
Recording snapshot = recorder.takeSnapshot();
snapshot.setName("Snapshot");
// Dump snapshot
snapshot.dump(Paths.get("snapshot.jfr"));
snapshot.close();
6.6. Copying Recordings#
import jdk.jfr.Recording;
Recording recording = new Recording();
recording.setName("Original Recording");
recording.start();
// ... Application runs ...
// Create copy of recording (do not stop original recording)
Recording copy = recording.copy(false);
copy.setName("Copy of Original");
copy.dump(Paths.get("copy.jfr"));
copy.close();
// Original recording continues running
// ...
recording.stop();
recording.dump(Paths.get("original.jfr"));
recording.close();
6.7. Creating Custom Events#
6.7.1. Basic Custom Event#
import jdk.jfr.Event;
import jdk.jfr.Label;
import jdk.jfr.Description;
@Label("User Action")
@Description("Records user actions in the application")
public class UserActionEvent extends Event {
@Label("Action Type")
@Description("Type of user action")
public String actionType;
@Label("User ID")
@Description("Identifier of the user")
public String userId;
@Label("Timestamp")
@Description("When the action occurred")
public long timestamp;
}
// Use event
public void performUserAction(String userId, String actionType) {
UserActionEvent event = new UserActionEvent();
event.actionType = actionType;
event.userId = userId;
event.timestamp = System.currentTimeMillis();
event.commit(); // Commit event
}
6.7.2. Duration Event#
import jdk.jfr.Event;
import jdk.jfr.Label;
import jdk.jfr.Description;
@Label("Database Query")
@Description("Records database query execution")
public class DatabaseQueryEvent extends Event {
@Label("Query SQL")
public String sql;
@Label("Rows Returned")
public int rowsReturned;
public void begin() {
super.begin();
}
public void end() {
super.end();
}
}
// Use duration event
public List<Row> executeQuery(String sql) {
DatabaseQueryEvent event = new DatabaseQueryEvent();
event.sql = sql;
event.begin();
try {
List<Row> rows = database.execute(sql);
event.rowsReturned = rows.size();
return rows;
} finally {
event.end();
event.commit();
}
}
6.7.3. Using shouldCommit() to Optimize Performance#
import jdk.jfr.Event;
public class ExpensiveEvent extends Event {
public String expensiveData;
public void recordExpensiveOperation() {
ExpensiveEvent event = new ExpensiveEvent();
// Check if event will be recorded
if (event.shouldCommit()) {
// Only execute expensive operation if event will be recorded
event.expensiveData = computeExpensiveData();
event.commit();
}
}
private String computeExpensiveData() {
// Expensive computation operation
return "expensive data";
}
}
6.7.4. Registering Custom Events#
import jdk.jfr.FlightRecorder;
import jdk.jfr.Event;
// Register custom event class (optional, usually auto-registered)
FlightRecorder.register(UserActionEvent.class);
// Use event
UserActionEvent event = new UserActionEvent();
event.actionType = "login";
event.userId = "user123";
event.commit();
6.8. Reading JFR Files#
6.8.1. Reading Events Using RecordingFile#
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.consumer.RecordedEvent;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
Path recordingPath = Paths.get("recording.jfr");
try (RecordingFile recordingFile = new RecordingFile(recordingPath)) {
// Read all events
while (recordingFile.hasMoreEvents()) {
RecordedEvent event = recordingFile.readEvent();
// Get event information
String eventName = event.getEventType().getName();
long startTime = event.getStartTime().toEpochMilli();
// Get event fields
if (eventName.equals("jdk.ThreadSleep")) {
Duration duration = event.getDuration();
System.out.println("Thread sleep: " + duration.toMillis() + " ms");
} else if (eventName.equals("jdk.CPULoad")) {
double jvmUser = event.getDouble("jvmUser");
double jvmSystem = event.getDouble("jvmSystem");
System.out.println("CPU Load - User: " + jvmUser + "%, System: " + jvmSystem + "%");
}
}
} catch (IOException e) {
e.printStackTrace();
}
6.8.2. Filtering Specific Events#
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.consumer.RecordedEvent;
import java.nio.file.Paths;
import java.io.IOException;
try (RecordingFile recordingFile = new RecordingFile(Paths.get("recording.jfr"))) {
while (recordingFile.hasMoreEvents()) {
RecordedEvent event = recordingFile.readEvent();
// Only process specific events
String eventName = event.getEventType().getName();
if (eventName.equals("jdk.GarbageCollection")) {
String gcName = event.getString("name");
Duration duration = event.getDuration();
System.out.println("GC: " + gcName + ", Duration: " + duration.toMillis() + " ms");
}
}
} catch (IOException e) {
e.printStackTrace();
}
6.8.3. Reading Event Type Information#
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.EventType;
import java.nio.file.Paths;
import java.io.IOException;
import java.util.List;
try (RecordingFile recordingFile = new RecordingFile(Paths.get("recording.jfr"))) {
// Get all event types
List<EventType> eventTypes = recordingFile.readEventTypes();
for (EventType eventType : eventTypes) {
System.out.println("Event: " + eventType.getName());
System.out.println(" Label: " + eventType.getLabel());
System.out.println(" Description: " + eventType.getDescription());
}
} catch (IOException e) {
e.printStackTrace();
}
6.9. Stream Processing (RecordingStream)#
RecordingStream (JDK 14+) provides stream processing capability for JFR events, can read events in real-time from current JVM.
6.9.1. Basic Stream Processing#
import jdk.jfr.consumer.RecordingStream;
import jdk.jfr.consumer.RecordedEvent;
import java.time.Duration;
try (RecordingStream stream = new RecordingStream()) {
// Enable events
stream.enable("jdk.ThreadSleep");
stream.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
// Register event handlers
stream.onEvent("jdk.ThreadSleep", event -> {
Duration duration = event.getDuration();
System.out.println("Thread sleep: " + duration.toMillis() + " ms");
});
stream.onEvent("jdk.CPULoad", event -> {
double jvmUser = event.getDouble("jvmUser");
System.out.println("CPU Load: " + jvmUser + "%");
});
// Start stream (asynchronous processing)
stream.startAsync();
// Run for some time
Thread.sleep(Duration.ofMinutes(5).toMillis());
// Stop stream
stream.stop();
} catch (Exception e) {
e.printStackTrace();
}
6.9.2. Create Stream Using Configuration#
import jdk.jfr.Configuration;
import jdk.jfr.consumer.RecordingStream;
import jdk.jfr.consumer.RecordedEvent;
import java.io.IOException;
import java.text.ParseException;
try {
// Load configuration
Configuration config = Configuration.getConfiguration("default");
// Create stream using configuration
try (RecordingStream stream = new RecordingStream(config)) {
// Register event handler for all events
stream.onEvent(event -> {
System.out.println("Event: " + event.getEventType().getName());
});
// Set maximum retention time
stream.setMaxAge(Duration.ofMinutes(10));
// Start stream
stream.startAsync();
// Run
Thread.sleep(Duration.ofMinutes(5).toMillis());
stream.stop();
}
} catch (IOException | ParseException e) {
e.printStackTrace();
}
6.9.3. Stream Processing and Dump#
import jdk.jfr.consumer.RecordingStream;
import jdk.jfr.consumer.RecordedEvent;
import java.nio.file.Paths;
import java.time.Duration;
try (RecordingStream stream = new RecordingStream()) {
stream.enable("jdk.ThreadSleep");
stream.enable("jdk.CPULoad");
// Set maximum retention time
stream.setMaxAge(Duration.ofMinutes(10));
// Register event handler
stream.onEvent(event -> {
// Process event
System.out.println("Event: " + event.getEventType().getName());
});
// Start stream
stream.start();
// Run for some time
Thread.sleep(Duration.ofMinutes(5).toMillis());
// Dump to file
stream.dump(Paths.get("stream-recording.jfr"));
// Stop stream
stream.stop();
} catch (Exception e) {
e.printStackTrace();
}
6.10. Complete Example#
The following is a complete example demonstrating how to use JFR API for performance monitoring:
import jdk.jfr.*;
import jdk.jfr.consumer.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
public class JFRExample {
// Custom event
@Label("Business Operation")
@Description("Records business operations")
static class BusinessOperationEvent extends Event {
@Label("Operation Name")
String operationName;
@Label("Result")
String result;
}
public static void main(String[] args) throws Exception {
// 1. Create recording
Recording recording = new Recording();
recording.setName("Performance Monitoring");
// 2. Configure events
recording.enable("jdk.ThreadSleep")
.withThreshold(Duration.ofMillis(20))
.withStackTrace();
recording.enable("jdk.CPULoad")
.withPeriod(Duration.ofSeconds(1));
// 3. Set recording options
recording.setMaxAge(Duration.ofHours(1));
recording.setMaxSize(100 * 1024 * 1024); // 100MB
// 4. Start recording
recording.start();
System.out.println("Recording started");
// 5. Execute business logic
performBusinessOperations();
// 6. Stop recording
recording.stop();
System.out.println("Recording stopped");
// 7. Dump recording
Path destination = Paths.get("performance.jfr");
recording.dump(destination);
System.out.println("Recording dumped to: " + destination);
// 8. Read and analyze recording
analyzeRecording(destination);
// 9. Close recording
recording.close();
}
private static void performBusinessOperations() throws InterruptedException {
// Simulate business operations
for (int i = 0; i < 10; i++) {
BusinessOperationEvent event = new BusinessOperationEvent();
event.begin();
// Simulate operation
Thread.sleep(100);
event.operationName = "Operation " + i;
event.result = "Success";
event.end();
event.commit();
}
}
private static void analyzeRecording(Path recordingPath) throws IOException {
try (RecordingFile recordingFile = new RecordingFile(recordingPath)) {
int threadSleepCount = 0;
int cpuLoadCount = 0;
while (recordingFile.hasMoreEvents()) {
RecordedEvent event = recordingFile.readEvent();
String eventName = event.getEventType().getName();
if (eventName.equals("jdk.ThreadSleep")) {
threadSleepCount++;
} else if (eventName.equals("jdk.CPULoad")) {
cpuLoadCount++;
}
}
System.out.println("Thread sleep events: " + threadSleepCount);
System.out.println("CPU load events: " + cpuLoadCount);
}
}
}
6.11. Notes#
Resource Management:
RecordingandRecordingFileimplement theCloseableinterface, should use try-with-resources statements to ensure resources are properly releasedRecording State: Check recording state before calling methods, certain operations can only be executed in specific states
Performance Overhead: Although JFR has low overhead, creating event objects in high-frequency paths still has overhead, using
shouldCommit()can optimize performanceEvent Field Types: Event fields only support specific types (primitive types, String, Thread, Class), other types will be ignored
Thread Safety: Methods of
Recordingclass are not thread-safe, if accessed from multiple threads, synchronization is neededStreaming:
RecordingStreamis suitable for real-time monitoring scenarios, but pay attention to settingmaxAgeormaxSizeto avoid excessive memory usage
7. Using JFR via JMX#
JMX (Java Management Extensions) is a management and monitoring framework provided by the Java platform. JFR exposes a complete JMX management interface through the FlightRecorderMXBean interface, allowing remote or local management of JFR recordings via JMX.
ObjectName: jdk.management.jfr:type=FlightRecorder
7.1. Obtaining FlightRecorderMXBean#
7.1.1. Local Access (Same JVM Process)#
import java.lang.management.ManagementFactory;
import jdk.management.jfr.FlightRecorderMXBean;
// Obtain via ManagementFactory (recommended)
FlightRecorderMXBean bean = ManagementFactory.getPlatformMXBean(FlightRecorderMXBean.class);
// Or obtain via MBeanServer
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName("jdk.management.jfr:type=FlightRecorder");
FlightRecorderMXBean bean = JMX.newMXBeanProxy(server, objectName, FlightRecorderMXBean.class);
7.1.2. Remote Access (Different JVM Process)#
Method 1: Connect via JMX Service URL
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.JMX;
import javax.management.ObjectName;
import jdk.management.jfr.FlightRecorderMXBean;
// Create JMX Service URL
String host = "localhost";
int port = 9999;
String url = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";
// Connect and obtain MBeanServerConnection
JMXServiceURL jmxURL = new JMXServiceURL(url);
JMXConnector connector = JMXConnectorFactory.connect(jmxURL);
MBeanServerConnection connection = connector.getMBeanServerConnection();
// Obtain FlightRecorderMXBean
ObjectName objectName = new ObjectName("jdk.management.jfr:type=FlightRecorder");
FlightRecorderMXBean bean = JMX.newMXBeanProxy(connection, objectName, FlightRecorderMXBean.class);
Method 2: Connect via Attach API (Local Process)
import com.sun.tools.attach.VirtualMachine;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.JMX;
import javax.management.ObjectName;
import jdk.management.jfr.FlightRecorderMXBean;
// Attach to target JVM via PID
String pid = "12345";
VirtualMachine vm = VirtualMachine.attach(pid);
// Start local management agent and obtain JMX Service URL
String jmxServiceUrl = vm.startLocalManagementAgent();
// Connect and obtain FlightRecorderMXBean
JMXServiceURL jmxURL = new JMXServiceURL(jmxServiceUrl);
JMXConnector connector = JMXConnectorFactory.connect(jmxURL);
MBeanServerConnection connection = connector.getMBeanServerConnection();
ObjectName objectName = new ObjectName("jdk.management.jfr:type=FlightRecorder");
FlightRecorderMXBean bean = JMX.newMXBeanProxy(connection, objectName, FlightRecorderMXBean.class);
Enable Remote JMX Connection:
Target JVM needs to enable JMX remote connection:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
7.2. Recording Management#
7.2.1. Create Recording#
// Create new recording (not started)
long recordingId = bean.newRecording();
7.2.2. Start and Stop Recording#
// Start recording
bean.startRecording(recordingId);
// Stop recording (returns true if successfully stopped)
boolean stopped = bean.stopRecording(recordingId);
7.2.3. Close Recording#
// Close recording and release resources
bean.closeRecording(recordingId);
7.2.4. Clone Recording#
// Clone recording (without stopping original)
long cloneId = bean.cloneRecording(recordingId, false);
// Clone recording and stop clone
long cloneId = bean.cloneRecording(recordingId, true);
7.2.5. Create Snapshot#
// Create snapshot of all recording data
long snapshotId = bean.takeSnapshot();
7.3. Recording Configuration#
7.3.1. Recording Options#
Recording options control recording behavior (such as duration, maximum size, dump path, etc.).
Supported Options:
name: Recording name (String)maxAge: Maximum retention time (format:"2 h","24 h","2 d","0"means unlimited)maxSize: Maximum total size (format: byte count, such as"1000000000","0"means unlimited)dumpOnExit: Whether to dump on JVM exit ("true"or"false")destination: Dump file path (String, relative path relative to JVM startup directory)disk: Whether to write to disk ("true"or"false")duration: Recording duration (format:"60 s","10 m","4 h","0"means unlimited)
Set Recording Options:
import java.util.HashMap;
import java.util.Map;
Map<String, String> options = new HashMap<>();
options.put("name", "My Recording");
options.put("maxAge", "2 h");
options.put("maxSize", "500000000"); // 500MB
options.put("dumpOnExit", "true");
options.put("destination", "/path/to/recording.jfr");
options.put("disk", "true");
options.put("duration", "1 h");
bean.setRecordingOptions(recordingId, options);
Get Recording Options:
Map<String, String> options = bean.getRecordingOptions(recordingId);
System.out.println("Name: " + options.get("name"));
System.out.println("Max Age: " + options.get("maxAge"));
System.out.println("Max Size: " + options.get("maxSize"));
7.3.2. Recording Settings#
Recording settings control which events are recorded and how they are recorded (such as event thresholds, sampling intervals, etc.).
Setting Format: <event-name>#<setting-name>=<value>
Set Recording Settings:
Map<String, String> settings = new HashMap<>();
settings.put("jdk.ThreadSleep#enabled", "true");
settings.put("jdk.ThreadSleep#threshold", "20 ms");
settings.put("jdk.ThreadSleep#stackTrace", "true");
settings.put("jdk.CPULoad#enabled", "true");
settings.put("jdk.CPULoad#period", "1 s");
bean.setRecordingSettings(recordingId, settings);
Get Recording Settings:
Map<String, String> settings = bean.getRecordingSettings(recordingId);
for (Map.Entry<String, String> entry : settings.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
7.3.3. Use Predefined Configuration#
// Use predefined configuration (such as "default" or "profile")
bean.setPredefinedConfiguration(recordingId, "default");
7.3.4. Use Custom Configuration (JFC File Contents)#
// Load JFC configuration contents from string
String jfcContents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<configuration version=\"2.0\" label=\"Custom\" description=\"Custom configuration\">\n" +
" <event name=\"jdk.ThreadSleep\">\n" +
" <setting name=\"enabled\">true</setting>\n" +
" <setting name=\"threshold\">20 ms</setting>\n" +
" </event>\n" +
"</configuration>";
bean.setConfiguration(recordingId, jfcContents);
7.4. Query Information#
7.4.1. Get All Recordings#
import java.util.List;
import jdk.management.jfr.RecordingInfo;
List<RecordingInfo> recordings = bean.getRecordings();
for (RecordingInfo info : recordings) {
System.out.println("ID: " + info.getId());
System.out.println("Name: " + info.getName());
System.out.println("State: " + info.getState());
System.out.println("Size: " + info.getSize());
System.out.println("Start Time: " + info.getStartTime());
}
RecordingInfo Fields:
id: Recording IDname: Recording namestate: Recording state (NEW,DELAYED,RUNNING,STOPPED,CLOSED)size: Recording size (bytes)startTime: Start time (millisecond timestamp)stopTime: Stop time (millisecond timestamp)duration: Duration (seconds)maxAge: Maximum retention time (seconds)maxSize: Maximum size (bytes)dumpOnExit: Whether to dump on exittoDisk: Whether to write to diskdestination: Dump destination pathsettings: Event settings (Map<String, String>)
7.4.2. Get Predefined Configurations#
import java.util.List;
import jdk.management.jfr.ConfigurationInfo;
List<ConfigurationInfo> configs = bean.getConfigurations();
for (ConfigurationInfo config : configs) {
System.out.println("Name: " + config.getName());
System.out.println("Label: " + config.getLabel());
System.out.println("Description: " + config.getDescription());
}
ConfigurationInfo Fields:
name: Configuration name (such as"default","profile")label: Display labeldescription: Description informationprovider: Provider (such as"OpenJDK")contents: JFC file contents (XML string)settings: Configured event settings (Map<String, String>)
7.4.3. Get Event Types#
import java.util.List;
import jdk.management.jfr.EventTypeInfo;
List<EventTypeInfo> eventTypes = bean.getEventTypes();
for (EventTypeInfo eventType : eventTypes) {
System.out.println("Name: " + eventType.getName());
System.out.println("Label: " + eventType.getLabel());
System.out.println("Description: " + eventType.getDescription());
// Get event setting descriptors
List<SettingDescriptorInfo> settings = eventType.getSettingDescriptors();
for (SettingDescriptorInfo setting : settings) {
System.out.println(" Setting: " + setting.getName() +
" (default: " + setting.getDefaultValue() + ")");
}
}
EventTypeInfo Fields:
id: Event type IDname: Event name (such as"jdk.ThreadSleep")label: Display labeldescription: Description informationcategoryNames: Category name listsettingDescriptors: Setting descriptor list
7.5. Streaming#
JMX provides streaming interface to read data in real-time from running recordings.
7.5.1. Open Stream#
import java.util.HashMap;
import java.util.Map;
import java.time.Instant;
// Stream options
Map<String, String> streamOptions = new HashMap<>();
streamOptions.put("startTime", "2020-03-17T09:00:00"); // ISO-8601 format
streamOptions.put("endTime", "2020-03-17T10:00:00");
streamOptions.put("blockSize", "50000"); // Maximum bytes per read
streamOptions.put("streamVersion", "1.0"); // Must be "1.0" to read from running recordings
// Open stream (recordingId=0 means read data from all recordings)
long streamId = bean.openStream(recordingId, streamOptions);
Stream Options:
startTime: Start time (ISO-8601 format or millisecond timestamp, default:Instant.MIN)endTime: End time (ISO-8601 format or millisecond timestamp, default:Instant.MAX)blockSize: Maximum bytes per read (default:50000)streamVersion: Stream version ("1.0"means can read from running recordings)
Notes:
- If
streamVersionis"1.0", can read data from running recordings - If
streamVersionis not specified, recording must be stopped before opening stream
7.5.2. Read Stream Data#
import java.io.FileOutputStream;
import java.io.IOException;
// Read stream data and write to file
try (FileOutputStream fos = new FileOutputStream("recording.jfr")) {
while (true) {
byte[] data = bean.readStream(streamId);
if (data == null) {
break; // No more data
}
fos.write(data);
}
}
7.5.3. Close Stream#
bean.closeStream(streamId);
7.6. Dump Recording#
// Dump recording to file (on target JVM's machine)
bean.copyTo(recordingId, "/path/to/recording.jfr");
Note: If called via remote JMX, file will be written to the machine where target JVM is running, not the client machine.
7.7. Notification Mechanism#
FlightRecorderMXBean implements the NotificationEmitter interface, can listen to recording state changes.
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
// Create notification listener
NotificationListener listener = new NotificationListener() {
@Override
public void handleNotification(Notification notification, Object handback) {
System.out.println("Notification: " + notification.getMessage());
System.out.println("Type: " + notification.getType());
System.out.println("User Data: " + notification.getUserData());
}
};
// Add notification listener
bean.addNotificationListener(listener, null, null);
// Remove notification listener
bean.removeNotificationListener(listener);
Notification Type: AttributeChangeNotification.ATTRIBUTE_CHANGE
Notification Content: When recording state changes (such as start, stop, close), notifications are sent.
7.8. RemoteRecordingStream (JDK 16+)#
RemoteRecordingStream provides a higher-level streaming interface, similar to local RecordingStream, but can be used via JMX remote connection.
7.8.1. Basic Usage#
import jdk.management.jfr.RemoteRecordingStream;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.MBeanServerConnection;
import java.time.Duration;
// Connect to remote JVM
String host = "localhost";
int port = 9999;
String url = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";
JMXServiceURL jmxURL = new JMXServiceURL(url);
JMXConnector connector = JMXConnectorFactory.connect(jmxURL);
MBeanServerConnection connection = connector.getMBeanServerConnection();
// Create remote recording stream
try (RemoteRecordingStream stream = new RemoteRecordingStream(connection)) {
// Enable events
stream.enable("jdk.GCPhasePause").withoutThreshold();
stream.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
// Register event handlers
stream.onEvent("jdk.CPULoad", event -> {
System.out.println("CPU Load: " + event);
});
stream.onEvent("jdk.GCPhasePause", event -> {
System.out.println("GC Pause: " + event);
});
// Start stream
stream.start();
// Run for a while
Thread.sleep(Duration.ofMinutes(5).toMillis());
}
7.8.2. Use Configuration#
try (RemoteRecordingStream stream = new RemoteRecordingStream(connection)) {
// Listen to metadata events to get configuration
stream.onMetadata(metadataEvent -> {
for (Configuration config : metadataEvent.getConfigurations()) {
if (config.getName().equals("default")) {
stream.setSettings(config.getSettings());
}
}
});
stream.onEvent(System.out::println);
stream.start();
Thread.sleep(Duration.ofMinutes(5).toMillis());
}
7.8.3. Specify Temporary Directory#
import java.nio.file.Path;
import java.nio.file.Paths;
Path tempDir = Paths.get("/tmp/jfr-remote");
try (RemoteRecordingStream stream = new RemoteRecordingStream(connection, tempDir)) {
stream.enable("jdk.ThreadSleep");
stream.onEvent(System.out::println);
stream.start();
Thread.sleep(Duration.ofMinutes(5).toMillis());
}
7.9. Complete Example#
The following is a complete example showing how to manage JFR recordings via JMX:
import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jdk.management.jfr.FlightRecorderMXBean;
import jdk.management.jfr.RecordingInfo;
public class JFRJMXExample {
public static void main(String[] args) throws Exception {
// 1. Get FlightRecorderMXBean
FlightRecorderMXBean bean = ManagementFactory.getPlatformMXBean(FlightRecorderMXBean.class);
// 2. Create new recording
long recordingId = bean.newRecording();
System.out.println("Created recording: " + recordingId);
// 3. Configure recording options
Map<String, String> options = new HashMap<>();
options.put("name", "JMX Recording");
options.put("maxAge", "1 h");
options.put("maxSize", "100000000"); // 100MB
options.put("dumpOnExit", "true");
options.put("destination", "jmx-recording.jfr");
options.put("disk", "true");
options.put("duration", "30 m");
bean.setRecordingOptions(recordingId, options);
// 4. Configure recording settings
Map<String, String> settings = new HashMap<>();
settings.put("jdk.ThreadSleep#enabled", "true");
settings.put("jdk.ThreadSleep#threshold", "20 ms");
settings.put("jdk.ThreadSleep#stackTrace", "true");
settings.put("jdk.CPULoad#enabled", "true");
settings.put("jdk.CPULoad#period", "1 s");
bean.setRecordingSettings(recordingId, settings);
// 5. Start recording
bean.startRecording(recordingId);
System.out.println("Recording started");
// 6. Query recording information
List<RecordingInfo> recordings = bean.getRecordings();
for (RecordingInfo info : recordings) {
if (info.getId() == recordingId) {
System.out.println("Recording Name: " + info.getName());
System.out.println("Recording State: " + info.getState());
System.out.println("Recording Size: " + info.getSize());
}
}
// 7. Run for a while
Thread.sleep(60000); // 1 minute
// 8. Stop recording
bean.stopRecording(recordingId);
System.out.println("Recording stopped");
// 9. Dump recording
bean.copyTo(recordingId, "jmx-recording.jfr");
System.out.println("Recording dumped to jmx-recording.jfr");
// 10. Close recording
bean.closeRecording(recordingId);
System.out.println("Recording closed");
}
}
7.10. Notes#
Remote File Path: When calling
copyTo()via remote JMX, file will be written to the machine where target JVM is running, not the client machine.Stream Version: To read data from running recordings, must set
streamVersion="1.0".Resource Management: After using streams, should call
closeStream()to release resources.Notification Listeners: If no longer need to listen to notifications, should remove listeners to avoid memory leaks.
Exception Handling: JMX operations may throw
IOException,IllegalArgumentException,IllegalStateExceptionand other exceptions, need appropriate handling.Connection Management: When using remote JMX, need to manage lifecycle of
JMXConnector, should close connection after use.Permission Requirements: Remote JMX connections may require authentication and SSL configuration, depending on target JVM configuration.
RemoteRecordingStream:
RemoteRecordingStreamimplements theAutoCloseableinterface, should use try-with-resources statements to ensure resources are properly released.
8. Analyzing JFR Files via jfr Tool#
jfr is a command-line tool provided by JDK for printing, analyzing, and manipulating JFR recording files (.jfr). The main function of the jfr tool is to convert binary format JFR files to human-readable format, and provide filtering, summarization, cleaning, merging, and splitting capabilities.
Basic Syntax:
jfr <command> [options] [file]
View Help Information:
# View all available commands
jfr help
# View help for specific command
jfr help print
jfr help view
jfr help summary
jfr help metadata
jfr help scrub
jfr help assemble
jfr help disassemble
jfr help configure
# View version information
jfr version
# or
jfr --version
8.1. jfr print (Print Events)#
The jfr print command is used to print the contents of JFR recording files to standard output.
Syntax:
jfr print [--xml|--json|--exact] [--categories <filter>] [--events <filter>] [--stack-depth <depth>] <file>
Options:
--xml: Print recording in XML format--json: Print recording in JSON format--exact: Print numbers and timestamps with full precision--categories <filter>: Select events matching category names- Filter is a comma-separated list of names, supports simple names, qualified names, and quoted glob patterns
- Example:
--categories "GC,JVM,Java*"
--events <filter>: Select events matching event names- Filter is a comma-separated list of names, supports simple names, qualified names, and quoted glob patterns
- Example:
--events "jdk.ThreadSleep,jdk.CPULoad",--events "jdk.*"
--stack-depth <depth>: Number of frames for stack traces (default:5)
Default Format: If --xml or --json is not specified, default uses human-readable format.
Filter Description:
- Filters can be based on event symbolic names (set via
@Nameannotation) or category names (set via@Categoryannotation) - If multiple filters are used, all matching events are included (union)
- If both category filter and event filter are used, selected events are the union of both filters
- If no filter is used, all events are printed
Usage Examples:
# Print all events (default format)
jfr print recording.jfr
# Print specific events
jfr print --events jdk.ThreadSleep recording.jfr
# Print multiple events
jfr print --events CPULoad,GarbageCollection recording.jfr
# Print events of specific categories
jfr print --categories "GC,JVM,Java*" recording.jfr
# Use both category and event filters
jfr print --categories GC --events CPULoad recording.jfr
# Use glob patterns
jfr print --events "jdk.*" --stack-depth 64 recording.jfr
# Output in JSON format
jfr print --json --events CPULoad recording.jfr
# Output in XML format
jfr print --xml --events jdk.ThreadSleep recording.jfr
# Use full precision
jfr print --exact --events "jdk.*" --stack-depth 64 recording.jfr
Output Format Description:
- Human-readable format (default): Formatted event values, for example field values with
@Percentageannotation0.52will be displayed as52% - XML format: Machine-readable XML format, can be further parsed or processed
- JSON format: Machine-readable JSON format, can be further parsed or processed
Stack Traces: By default, stack traces are truncated to 5 frames, can be increased or decreased via --stack-depth option.
8.2. jfr view (View Aggregated Views)#
The jfr view command is used to aggregate and display event data in predefined view formats.
Syntax:
jfr view [--verbose] [--width <integer>] [--truncate <mode>] [--cell-height <integer>] <view> <file>
Options:
--verbose: Display query information that constitutes the view--width <integer>: View width (character count, default value depends on view)--truncate <mode>: How to truncate content that exceeds table cell spacebeginning: Truncate from beginningend: Truncate from end (default)
--cell-height <integer>: Maximum number of lines for table cells (default value depends on view)
View Parameter:
<view>(required): View name or event type name- Predefined views:
gc,hot-methods,allocation-by-class,contention-by-site, etc. - Event types:
jdk.GarbageCollection,jdk.ThreadStart, etc. - Special values:
types: List all available event typesall-views: Display all predefined viewsall-events: Display all events
- Predefined views:
Predefined View Categories:
JVM Views (jvm.*):
gc: Garbage collection statisticsgc-pauses: GC pause statisticsgc-configuration: GC configuration informationgc-parallel-phases: Parallel GC phasesgc-concurrent-phases: Concurrent GC phasesgc-pause-phases: GC pause phasesgc-references: GC reference statisticsgc-allocation-trigger: GC allocation triggergc-cpu-time: GC CPU timeheap-configuration: Heap configurationcompiler-configuration: Compiler configurationcompiler-statistics: Compiler statisticscompiler-phases: Compiler phaseslongest-compilations: Longest compilationssafepoints: Safepointsvm-operations: VM operationsdeoptimizations-by-reason: Deoptimizations by reasondeoptimizations-by-site: Deoptimizations by siteclass-modifications: Class modificationsblocked-by-system-gc: Blocked by System.gc()native-memory-committed: Committed native memorynative-memory-reserved: Reserved native memorytlabs: Thread local allocation buffers
Environment Views (environment.*):
cpu-load: CPU loadcpu-load-samples: CPU load samplescpu-information: CPU informationcpu-tsc: CPU timestamp countersystem-information: System informationsystem-properties: System propertiessystem-processes: System processesenvironment-variables: Environment variablesnetwork-utilization: Network utilizationnative-libraries: Native librariesnative-library-failures: Native library load/unload failurescontainer-configuration: Container configurationcontainer-cpu-usage: Container CPU usagecontainer-memory-usage: Container memory usagecontainer-io-usage: Container I/O usagecontainer-cpu-throttling: Container CPU throttlingrecording: Recording informationactive-recordings: Active recordingsactive-settings: Active settingsjvm-flags: JVM flagsjvm-information: JVM informationevents-by-count: Event types by countevents-by-name: Event types by name
Application Views (application.*):
hot-methods: Hot methods (most executed Java methods)cpu-time-hot-methods: CPU time hot methodscpu-time-statistics: CPU time sampling statisticsallocation-by-class: Allocation by classallocation-by-thread: Allocation by threadallocation-by-site: Allocation by sitecontention-by-thread: Lock contention by threadcontention-by-class: Lock contention by lock classcontention-by-site: Lock contention by sitecontention-by-address: Lock contention by monitor addressexception-count: Exception statisticsexception-by-type: Exceptions by typeexception-by-message: Exceptions by messageexception-by-site: Exceptions by sitememory-leaks-by-class: Memory leak candidates by classmemory-leaks-by-site: Memory leak candidates by sitethread-count: Java thread statisticsthread-allocation: Thread allocation statisticsthread-cpu-load: Thread CPU loadthread-start: Platform thread startpinned-threads: Pinned virtual threadsfile-reads-by-path: File reads by pathfile-writes-by-path: File writes by pathsocket-reads-by-host: Socket reads by hostsocket-writes-by-host: Socket writes by hostclass-loaders: Class loaderslongest-class-loading: Longest class loadingmodules: Modulesmonitor-inflation: Monitor inflationnative-methods: Waiting or executing native methodsobject-statistics: Objects occupying more than 1%finalizers: Finalizersdeprecated-methods-for-removal: Deprecated methods marked for removalmethod-timing: Method timingmethod-calls: Method callslatencies-by-type: Latencies by type
Usage Examples:
# View GC view
jfr view gc recording.jfr
# View hot methods view
jfr view hot-methods recording.jfr
# View allocation view (by class)
jfr view allocation-by-class recording.jfr
# View lock contention view (by site)
jfr view contention-by-site recording.jfr
# View specified event type
jfr view jdk.GarbageCollection recording.jfr
# View all available views
jfr view all-views recording.jfr
# View all event types
jfr view types recording.jfr
# View all events
jfr view all-events recording.jfr
# Custom view parameters
jfr view --width 160 hot-methods recording.jfr
# Display view query information
jfr view --verbose allocation-by-class recording.jfr
# Set truncation mode
jfr view --truncate beginning SystemProcess recording.jfr
# Set cell height
jfr view --cell-height 10 ThreadStart recording.jfr
Notes:
- View Queries: Each view is based on a query statement, can be viewed via
--verboseoption - Event Types: Can directly use event type names as views, will display all events of that event type
- Performance: For large recording files, some views may take a long time to process
8.3. jfr summary (Summary Statistics)#
The jfr summary command is used to print statistical information about recordings, such as the number of recorded events and the disk space they use.
Syntax:
jfr summary <file>
Output Content:
- Version Information: JFR file format version (such as
2.1) - Chunks Count: Number of chunks contained in the recording file
- Start Time: Recording start time (UTC)
- Duration: Recording duration (seconds)
- Event Statistics Table:
- Event type name
- Event count (Count)
- Event size (Size, bytes)
Usage Examples:
# View recording summary
jfr summary recording.jfr
Output Example:
Version: 2.1
Chunks: 1
Start: 2025-11-17 09:41:00 (UTC)
Duration: 165 s
Event Type Count Size (bytes)
=============================================================
jdk.NativeMethodSample 6992 79112
jdk.GCPhaseParallel 6127 150540
jdk.ModuleExport 1615 19253
jdk.SystemProcess 1032 119761
jdk.NativeLibrary 877 76574
...
Notes:
- File Integrity: If viewing the last file in the repository, may indicate file corruption (because metadata hasn’t been flushed yet), this is normal, flush is executed every 1 second by default, try a few more times to see correct results
- Sorting: Event statistics table is sorted by event count in descending order
8.4. jfr metadata (Metadata Information)#
The jfr metadata command is used to display event metadata information, such as event names, categories, and field layouts.
Syntax:
jfr metadata [--categories <filter>] [--events <filter>] [<file>]
Options:
--categories <filter>: Select events matching category names--events <filter>: Select events matching event names<file>(optional): JFR recording file path- If omitted, uses metadata from the JDK running the
jfrtool
- If omitted, uses metadata from the JDK running the
Output Content:
- Event type information:
- Event name, label, description
- Category names
- Field information (field name, type, label, description)
- Setting descriptors (such as
enabled,threshold,period, etc.)
Usage Examples:
# Display all event metadata (using metadata from JDK)
jfr metadata
# Display event metadata from recording file
jfr metadata recording.jfr
# Display metadata for specific events
jfr metadata --events jdk.ThreadStart recording.jfr
# Display metadata for multiple events
jfr metadata --events CPULoad,GarbageCollection recording.jfr
# Display event metadata for specific categories
jfr metadata --categories "GC,JVM,Java*" recording.jfr
# Use glob patterns
jfr metadata --events "Thread*" recording.jfr
Notes:
- Metadata Source: If file is not specified, uses metadata from the JDK running the
jfrtool; if file is specified, uses metadata from the file - Field Layout: Metadata contains complete field layout information for events, helpful for understanding event structure
8.5. jfr scrub (Clean Recording)#
The jfr scrub command is used to remove sensitive content from recording files or reduce file size.
Syntax:
jfr scrub [--include-events <filter>] [--exclude-events <filter>]
[--include-categories <filter>] [--exclude-categories <filter>]
[--include-threads <filter>] [--exclude-threads <filter>]
<input-file> [<output-file>]
Options:
--include-events <filter>: Select events matching event names (only keep these events)--exclude-events <filter>: Exclude events matching event names (remove these events)--include-categories <filter>: Select events matching category names (only keep these events)--exclude-categories <filter>: Exclude events matching category names (remove these events)--include-threads <filter>: Select events matching thread names (only keep events from these threads)--exclude-threads <filter>: Exclude events matching thread names (remove events from these threads)<input-file>(required): Input file path<output-file>(optional): Output file path- If not specified, will append
-scrubbedto input file path as output filename
- If not specified, will append
Filter Description:
- Filters are comma-separated lists of names, supports simple names, qualified names, and quoted glob patterns
- If multiple filters are used, they are applied in specified order
includeandexcludecan be combined
Usage Examples:
# Only keep socket-related events
jfr scrub --include-events 'jdk.Socket*' recording.jfr socket-only.jfr
# Remove environment variable events containing passwords
jfr scrub --exclude-events InitialEnvironmentVariable recording.jfr no-psw.jfr
# Only keep events from main thread
jfr scrub --include-threads main recording.jfr
# Exclude events from specific threads
jfr scrub --exclude-threads 'Foo*' recording.jfr
# Only keep events from specific categories
jfr scrub --include-categories 'My App' recording.jfr
# Exclude events from specific categories
jfr scrub --exclude-categories JVM,OS recording.jfr
# Combine multiple filters
jfr scrub --include-events 'jdk.Socket*' --exclude-threads 'Worker*' recording.jfr filtered.jfr
Output Information:
After command execution, displays:
- Path of cleaned file
- Removed event statistics (event names and removal percentage)
Notes:
- File Size: Cleaned files are usually smaller than original files, because unnecessary events are removed
- Data Integrity: Cleaned files are still valid JFR files, can be analyzed normally with other
jfrcommands - Sensitive Information: Can be used to remove events containing sensitive information (such as environment variables, system properties, etc.)
8.6. jfr assemble (Assemble Recording)#
The jfr assemble command is used to assemble chunk files from repository into a complete JFR recording file.
Syntax:
jfr assemble <repository> <file>
Parameters:
<repository>(required): Repository directory path containing chunk files<file>(required): JFR recording file path to create (.jfrextension)
How It Works:
- Scan all
.jfrchunk files in repository directory - Sort by filename (maintain chronological order)
- Exclude incomplete chunk files (
.partfiles) - Concatenate all chunk files in chronological order
- Generate complete JFR recording file
Use Cases:
- JVM Crash Recovery: If JVM crashes, chunk files in repository can be recovered and assembled into complete recording file
- Manual Assembly: Manually create recording file from repository directory
Usage Examples:
# Assemble recording file from repository directory
jfr assemble ./2025_11_19_17_41_00_3833 assembled-recording.jfr
# Assemble from repository at specified path
jfr assemble /path/to/repository recording.jfr
Notes:
- Chunk Files: Only processes complete
.jfrfiles,.partfiles are excluded - Chronological Order: Chunk files are sorted by filename, ensuring correct chronological order
- File Integrity: Assembled file is a valid JFR file, can be analyzed normally with other
jfrcommands
8.7. jfr disassemble (Disassemble Recording)#
The jfr disassemble command is used to split JFR recording files into multiple smaller files/chunks.
Syntax:
jfr disassemble [--output <directory>] [--max-chunks <chunks>] [--max-size <size>] <file>
Options:
--output <directory>: Output directory (default: current directory)--max-chunks <chunks>: Maximum number of chunks per split file (default:5)- Chunk size varies by recording, typically around 15 MB
--max-size <size>: Maximum bytes per split file<file>(required): JFR recording file path to split
How It Works:
- Read recording file, identify all chunks
- Group chunks according to
--max-chunksor--max-sizeparameters - Create a new JFR file for each group
- File naming:
<original-filename>_1.jfr,<original-filename>_2.jfr, etc. - If chunk count exceeds 100, filenames are zero-padded to maintain chronological order (e.g.,
myfile_001.jfr)
Use Cases:
- Repair Corrupted Files: Repair corrupted recording files by removing corrupted chunks
- Reduce File Size: Split oversized files into smaller files for easier transmission
- Segmented Analysis: Split large files into small files for segmented analysis
Usage Examples:
# Split recording file (using default settings: maximum 5 chunks per file)
jfr disassemble recording.jfr
# Specify output directory
jfr disassemble --output ./split recording.jfr
# Specify maximum number of chunks per file
jfr disassemble --max-chunks 10 recording.jfr
# Specify maximum size per file (bytes)
jfr disassemble --max-size 50000000 recording.jfr
# Combine multiple options
jfr disassemble --output ./split --max-chunks 3 recording.jfr
Output File Naming:
- If recording contains ≤ 100 chunks:
recording_1.jfr,recording_2.jfr, … - If recording contains > 100 chunks:
recording_001.jfr,recording_002.jfr, …
Notes:
- File Integrity: Each split file is a valid JFR file, can be analyzed independently
- Chunk Boundaries: Splitting occurs at chunk boundaries, does not break chunk integrity
- Chronological Order: Numbers in filenames maintain chronological order
8.8. jfr configure (Configure JFC File)#
The jfr configure command is used to create and edit JFC configuration files. This command has been detailed in Section 5.2, here is only a brief description.
Syntax:
jfr configure [--interactive] [--verbose] [--input <files>] [--output <file>] [option=value]* [event-setting=value]*
Main Options:
--interactive: Interactive mode--verbose: Display modified settings--input <files>: Input JFC files (comma-separated)--output <file>: Output filename (default:custom.jfc)option=value: Modify JFC options (such asgc=high)event-setting=value: Modify event settings (such asjdk.ThreadSleep#threshold=50ms)
Usage Examples:
# Create configuration interactively (requires --interactive parameter)
jfr configure --interactive --input default.jfc --output custom.jfc
# Create configuration from command line
jfr configure --input default.jfc --output custom.jfc \
jdk.ThreadSleep#threshold=50ms \
jdk.CPULoad#period=2s
For detailed description, please refer to Section 5.2.
8.9. Complete Usage Flow Example#
The following is a complete usage flow example:
# 1. View recording summary
jfr summary recording.jfr
# 2. View all available views
jfr view all-views recording.jfr
# 3. View GC view
jfr view gc recording.jfr
# 4. View hot methods
jfr view hot-methods recording.jfr
# 5. Print specific events
jfr print --events jdk.ThreadSleep recording.jfr
# 6. Export events in JSON format
jfr print --json --events jdk.CPULoad recording.jfr > cpuload.json
# 7. View event metadata
jfr metadata --events jdk.ThreadSleep recording.jfr
# 8. Clean recording (remove sensitive information)
jfr scrub --exclude-events InitialEnvironmentVariable recording.jfr cleaned.jfr
# 9. Assemble recording from repository
jfr assemble ./repository assembled.jfr
# 10. Split large file
jfr disassemble --max-chunks 5 large-recording.jfr
8.10. Notes#
File Paths: File paths can be absolute or relative (relative to current working directory)
File Format: Input files must be valid JFR files (
.jfrextension)Filter Syntax:
- Supports simple names:
CPULoad - Supports qualified names:
jdk.CPULoad - Supports glob patterns:
jdk.*,Thread* - Multiple values separated by commas:
CPULoad,GarbageCollection - Use quotes when containing special characters:
"GC,JVM,Java*"
- Supports simple names:
Output Format:
jfr printoutputs to standard output by default, can be redirected to filejfr viewoutputs to standard outputjfr scrub,jfr assemble,jfr disassemblecreate new files
Performance Considerations:
- For large recording files, some operations (such as
jfr print,jfr view) may take a long time - Using filters can reduce amount of data processed, improve performance
- For large recording files, some operations (such as
File Integrity:
- If viewing files being written in repository, may indicate file corruption, this is normal
- Wait for file writing to complete before viewing, or use
jfr assembleto assemble complete file
Help Information: Use
jfr help <command>to view detailed help information for each command



