12 Periodic Events

The example PeriodicSample.java creates a periodic event named StartedThreadCount that records the total number of threads that have been created and started every second.

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;

import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.Label;
import jdk.jfr.Name;
import jdk.jfr.Period;

public class PeriodicSample {

    private static ThreadMXBean tBean =
        ManagementFactory.getThreadMXBean();

    @Name("com.oracle.StartedThreadCount")
    @Label("Total number of started threads")
    @Period("1 s")
    static class StartedThreadCount extends Event {
        long totalStartedThreadCount;
    }

    public static void main(String[] args) throws InterruptedException {

        Runnable hook = () -> {
            StartedThreadCount event = new StartedThreadCount();
            event.totalStartedThreadCount =
                tBean.getTotalStartedThreadCount();
            event.commit();
        };

        FlightRecorder.addPeriodicEvent(StartedThreadCount.class, hook);

        for (int i = 0; i < 4; i++) {
            Thread.sleep(1500);
            Thread t = new Thread();
            t.start();
        }
      
        FlightRecorder.removePeriodicEvent(hook);
    }
}

Run PeriodicSample with the following commands:

java -XX:StartFlightRecording:filename=periodic.jfr PeriodicSample.java
jfr print --events StartedThreadCount periodic.jfr 

The last command prints output similar to the following:

com.oracle.StartedThreadCount {
  startTime = 00:59:40.769
  totalStartedThreadCount = 12
    ...
}

com.oracle.StartedThreadCount {
  startTime = 00:59:41.816
  totalStartedThreadCount = 12
    ...
}

com.oracle.StartedThreadCount {
  startTime = 00:59:42.866
  totalStartedThreadCount = 13
    ...
}

com.oracle.StartedThreadCount {
  startTime = 00:59:43.918
  totalStartedThreadCount = 14
    ...
}

com.oracle.StartedThreadCount {
  startTime = 00:59:44.962
  totalStartedThreadCount = 14
    ...
}

To create a periodic event, follow these two steps:

  1. Specify how often the event should be emitted with the @Period annotation:
    @Name("com.oracle.StartedThreadCount")
    @Label("Total number of started threads")
    @Period("1 s")
    static class StartedThreadCount extends Event {
        long totalStartedThreadCount;
    }

    Valid units for a period are: ns, us, ms, s, m, h, and d.

    You can also specify one of the following:

    • everyChunk: A periodic event will be emitted at least once in the recording.
    • beginChunk: A periodic event will be emitted in the beginning of a recording.
    • endChunk: A periodic event will be emitted in the end of a recording.
  2. Add the periodic event with the FlightRecorder.addPeriodicEvent(Class<? extends Event>, Runnable) static method. The first argument is the name of the periodic event's class. The second argument is a callback method that's represented by a lambda expression that creates and commits the event:
    Runnable hook = () -> {
        StartedThreadCount event = new StartedThreadCount();
        event.totalStartedThreadCount =
            tBean.getTotalStartedThreadCount();
        event.commit();
    };
    
    FlightRecorder.addPeriodicEvent(StartedThreadCount.class, hook);

The method FlightRecorder.removePeriodicEvent(Runnable) removes the lambda expression associated with a periodic event. In most cases, you won't need this method; if you want to disable a periodic event, you can call Recording.disable(Class<? extends Event>). However, one reason to call removePeriodicEvent is to avoid memory leaks. For example, suppose you have an application server where data is loaded and unloaded. If the callback method references data that the server loads and unloads, then it may prevent that data from being garbage collected. You can avoid this by removing the callback method when the data is unloaded.