OpenJDK, JDK Flight Recorder

View Command

JDK 21 comes with a new JFR view command that displays aggregated event data in the shell. The command can be used to view information about an application without the need to dump a recording file, or open up JDK Mission Control.

$ jcmd 1739 JFR.view hot-methods

                         Java methods that execute the most

Method                                                        Samples Percent
------------------------------------------------------------- ------- -------
sun.java2d.marlin.Renderer._endRendering(...)                    1659  42.99%
sun.java2d.marlin.MarlinTileGenerator.getAlphaRLE(...)            592  15.34%
sun.java2d.marlin.MarlinCache.copyAARowRLE_WithBlockFlags(...)    447  11.58%
sun.java2d.marlin.MarlinCache.copyAARowNoRLE(...)                 246   6.37%
sun.java2d.marlin.Renderer.addLine(...)                            89   2.31%
sun.java2d.marlin.Renderer.copyAARow(...)                          86   2.23%
sun.java2d.marlin.ArrayCacheInt.fill(...)                          73   1.89%
...

To use the command, first start a recording:

$ java -XX:StartFlightRecording -jar my.jar

Once the application is running, the jcmd tool can be used to list all running Java processes:

$ jcmd  
37417 my.jar
37418 jdk.jcmd/sun.tools.jcmd.JCmd

Use the PID or the name of the jar/class file together with JFR.view and the name of the view to display, for example:

$ jcmd 37417 JFR.view allocation-by-site

                                     Allocation by Site

Method                                                  Allocation Pressure
------------------------------------------------------- -------------------
java.lang.StringUTF16.compress(char[], int, int)                     37.50%
java.lang.Integer.valueOf(int)                                       13.75%
spec.jbb.infra.Util.TransactionLogBuffer.getLine(...)                 9.22%
java.math.BigDecimal.valueOf(long, int)                               8.92%
spec.jbb.CustomerReportTransaction.process(...)                       7.55%
java.math.BigDecimal.layoutChars(boolean)                             5.71%
...

The allocation-by-site view is built from the jdk.ObjectAllocationSample event that was added in JDK 16. To see a list of all available views, just omit the view name. JDK 21 comes with 70 predefined views, with more to be added in future releases. The views are grouped into three categories: Java Virtual Machine, Environment, and Application.

$ jcmd 37417 JFR.view
...

Java virtual machine views:
 class-modifications       gc-concurrent-phases longest-compilations   
 compiler-configuration    gc-configuration     native-memory-committed
 compiler-phases           gc-cpu-time          native-memory-reserved 
 compiler-statistics       gc-pause-phases      safepoints             
 deoptimizations-by-reason gc-pauses            tlabs                  
 deoptimizations-by-site   gc-references        vm-operations          
 gc                        heap-configuration  

Environment views:
 active-recordings        cpu-information       jvm-flags          
 active-settings          cpu-load              native-libraries   
 container-configuration  cpu-load-samples      network-utilization
 container-cpu-throttling cpu-tsc               recording          
 container-cpu-usage      environment-variables system-information 
 container-io-usage       events-by-count       system-processes   
 container-memory-usage   events-by-name        system-properties  

Application views:
 allocation-by-class   exception-count       native-methods       
 allocation-by-site    file-reads-by-path    object-statistics    
 allocation-by-thread  file-writes-by-path   pinned-threads       
 class-loaders         finalizers            socket-reads-by-host 
 contention-by-address hot-methods           socket-writes-by-host
 contention-by-class   latencies-by-type     thread-allocation    
 longest-class-loading thread-count         
 contention-by-thread  memory-leaks-by-class thread-cpu-load      
 exception-by-message  memory-leaks-by-site  thread-start         
 exception-by-site     modules       

By default, a view will cover the last 32 MB of data, although not further back than 10 minutes. To set a custom time range, use the parameters maxage and maxsize. They work similarly to the maxage and maxsize parameters for the JFR.dump command.

$ jcmd my.jar JFR.view maxage=1h maxsize=2000MB gc-pauses

GC Pauses
---------

Total Pause Time: 2.93 s

Number of Pauses: 272

Minimum Pause Time: 0.471 ms

Median Pause Time: 7.82 ms

Average Pause Time: 10.8 ms

P90 Pause Time: 10.2 ms

P95 Pause Time: 20.9 ms

P99 Pause Time: 256 ms

P99.9% Pause Time: 256 ms

Maximum Pause Time: 256 ms

The format of the output can be controlled using the parameters width, cell-height and truncate. The width parameter sets the maximum number of characters to use, for example 120. If not set, the table will be sized automatically.

The cell-height parameter determines the number of lines to use inside a table cell. For most views, it’s 1 by default, but can be set to a higher value in case the text doesn’t fit. This option is especially useful for displaying multi-line stack traces.

Finally, the truncate parameter can be set to beginning or end to decide if the first or last characters should be omitted in case the text overflows. By default, truncation happens at the end.

$ jcmd my.jar JFR.view cell-height=3 truncate=beginning width=80 system-processes

                                System Processes

First Observed Last Observed PID   Command Line                                
-------------- ------------- ----- --------------------------------------------
13:22:57       13:24:29      13028 C:\Program Files (x86)\Intel\Intel(R) Rapid 
                                   Storage Technology enterprise\IAStorIcon.exe
13:22:57       13:24:29      16200 C:\Program Files\Git\git-bash.exe           
13:22:57       13:24:29      7232  C:\Program Files\Microsoft Visual Studio\202
                                   2\Enterprise\Common7\IDE\PerfWatson2.exe    
13:22:57       13:24:29      18216 C:\Program Files\Microsoft Visual Studio\202
                                   2\Enterprise\Common7\IDE\VC\vcpackages\x86\V
                                   CPkgSrv.exe                                 
13:22:57       13:24:29      8536  C:\Program Files\Microsoft Visual Studio\202
                                   2\Enterprise\Common7\IDE\devenv.exe         
13:22:57       13:24:29      15228 C:\Program Files\Microsoft Visual Studio\202
                                   2\Enterprise\Common7\ServiceHub\Hosts\Servic
                                   eHub.Host.CLR.AnyCPU\ServiceHub.Host.CLR.exe
13:22:57       13:24:29      14128 ...es\Microsoft Visual Studio\2022\Enterpris
                                   e\Common7\ServiceHub\Hosts\ServiceHub.Host.C
                                   LR.AnyCPU\ServiceHub.TestWindowStoreHost.exe

The view command can also be used against a recording file by using the bin/jfr tool. A file can be created by setting the filename parameter with the -XX:StartFlightRecording option. When the JVM exits, the recording contents are written to the file.

Instead of a view, the name of a JFR event can be specified. In the following example, a custom event is created using the jdk.jfr event API. In the rendered output, the column headers get their name from the Label annotation and the bytes formatting comes from the DataAmount annotation.

import jdk.jfr.*;

public class Example {

  @Name("example.FreeMemory")
  @Label("Free Memory")
  static class FreeMemoryEvent extends Event {
    @Description("An approximation of the amount of memory available for allocation")
    @Label("Free Memory")
    @DataAmount(DataAmount.BYTES)
    long free;
  }

  public static void main(String... args) throws Exception {
    for(int i = 0; i < 3; i++) {
      checkFreeMemory();
      Thread.sleep(1000);
    }
  }

  public static void checkFreeMemory() {
    FreeMemoryEvent event = new FreeMemoryEvent();
    event.begin();
    event.free = Runtime.getRuntime().freeMemory();
    event.commit();
  }
}

To run the program and display the events:

$ java -XX:StartFlightRecording:filename=recording.jfr Example.java
$ jfr view --cell-height 2 FreeMemory recording.jfr

                                  Free Memory

Start Time  Duration Event Thread     Stack Trace                   Free Memory
---------- --------- ---------------- ----------------------------- -----------
12:25:29   0.0213 ms main             Example.checkFreeMemory()        240.7 MB
                                      Example.main(String[])                   
12:25:30   0.0114 ms main             Example.checkFreeMemory()        238.0 MB
                                      Example.main(String[])                   
12:25:31   0.0218 ms main             Example.checkFreeMemory()        237.7 MB
                                      Example.main(String[])                   

To inspect what events are contained in a recording, use the summary command:

$ jfr summary recording.jfr

The parser that reads the file only parses the events that make up the view, which makes the process quick in most cases. Internally, the views are built using a query language. To see the query that makes up a view, specify the –verbose parameter.

$ jfr view --verbose gc recording.jfr

                                  Garbage Collections

Start        GC ID Type                     Heap Before GC Heap After GC  Longest Pause
(startTime) (gcId) (eventType.label)            (heapUsed)    (heapUsed) (longestPause)
----------- ------ ------------------------ -------------- ------------- --------------
13:16:44         0 Young Garbage Collection       409.7 MB        3.1 MB        10.0 ms
13:16:44         1 Old Garbage Collection           3.1 MB        2.6 MB        21.8 ms
13:16:45         2 Young Garbage Collection       330.4 MB        3.9 MB        1.40 ms
13:16:45         3 Old Garbage Collection           3.9 MB        3.5 MB        32.1 ms
13:16:45         4 Young Garbage Collection         3.5 MB        3.5 MB       0.471 ms
13:16:45         5 Old Garbage Collection           3.5 MB        3.5 MB        20.0 ms
13:16:58         6 Young Garbage Collection         2.0 GB       33.0 MB        29.4 ms
...


COLUMN 'Start', 'GC ID', 'Type', 'Heap Before GC', 'Heap After GC', 'Longest Pause'
FORMAT none, none, missing:Unknown, none, none, none SELECT G.startTime, gcId,
[Y|O].eventType.label, B.heapUsed, A.heapUsed, longestPause FROM GarbageCollection AS G,
GCHeapSummary AS B, GCHeapSummary AS A, OldGarbageCollection AS O,
YoungGarbageCollection AS Y WHERE B.when = 'Before GC' AND A.when = 'After GC' GROUP BY
gcId ORDER BY G.startTime

In the above example, the view consists of events from four different event types, jdk.GarbageCollection, jdk.GCHeapSummary, jdk.YoungGarbageCollection and jdk.OldGarbageCollection. The events are tied together using the gcId in the GROUP BY clause. The query language, currently experimental, is designed to simplify the process for OpenJDK engineers to define views. In the future, it may be exposed for end-users as well.

To try out the new view command, download builds of JDK 21

Command-Line Reference

jfr view [--verbose]
         [--width <integer>]
         [--truncate <mode>]
         [--cell-height <integer>]
         <view>
         <file>

  --verbose               Displays the query that makes up the view

  --width <integer>       The width of the view in characters. Default value depends on the view

  --truncate <mode>       How to truncate content that exceeds space in a table cell.
                          Mode can be 'beginning' or 'end'. Default value is 'end'

  --cell-height <integer> Maximum number of rows in a table cell. Default value depends on the view

  <view>                  Name of the view or event type to display. See list below for
                          available views

  <file>                  Location of the recording file (.jfr)




jcmd <pid> JFR.view [options]

Options:

cell-height   (Optional) Maximum number of rows in a table cell. (INTEGER, no default value)
   
maxage        (Optional) Length of time for the view to span. (INTEGER followed by  
              's' for seconds 'm' for minutes, or 'h' for hours; default value is 10m)
   
maxsize       (Optional) Maximum size for the view to span in bytes if one of
              the following suffixes is not used: 'm' or 'M' for megabytes OR
              'g' or 'G' for gigabytes. (STRING, default value is 32MB)

truncate      (Optional) How to truncate content that exceeds space in a table cell.
              Mode can be 'beginning' or 'end'. (STRING, default value 'end')

verbose       (Optional) Displays the query that makes up the view.
              (BOOLEAN, default value false)

<view>        (Mandatory) Name of the view or event type to display.
              See list below for available views. (STRING, no default value)

width         (Optional) The width of the view in characters.
              (INTEGER, no default value)