News and views from members of the Java team at Oracle
JDK Flight Recorder (JFR) is an event-based tool for monitoring and profiling built into the JDK. JFR has a very low overhead of <1% using default settings allowing it to be used in production. Typically extracting information out of JFR requires performing a dump; however, added in JDK 14, the package jdk.jfr.consumer provides APIs for the consumption of JFR events without requiring a JFR dump to be done. Let's take a look at how this API can be used.
The central class in the new Event Streaming API is the interface jdk.jfr.consumer.EventStream, which, as the name suggests, represents a stream of events.
EventStream provides three factory methods for creating a new EventStream instance:
openFile(java.nio.file.Path) - Creates an EventStream from a recording file.
openRepository() - Creates an EventStream from the repository of the current JVM.
openRepository(java.nio.file.Path) - Creates an EventStream from a directory.
There are also two public implementations which can be instantiated as well:
jdk.jfr.consumer.RecordingStream - A recording stream of the current JVM.
jdk.management.jfr.RemoteRecordingStream - A recording stream that can serialize events using a javax.management.MBeanServerConnection
Once an EventStream has been created, actions can be registered to it. There are two ways of registering actions for events:
onEvent(consumer) - all events will be registered to this action.
onEvent(String, Consumer) - only events whose name matches will be registered to this action.
Additionally actions can be registered for when other conditions are met:
onClose(Runnable) - Registers an action to perform when the stream is closed.
onError(Consumer<Throwable>) - Registers an action to perform if an exception occurs.
onFlush(Runnable) - Registers an action to perform after the stream has been flushed.
onMetadata(Consumer<MetadataEvent>) - Registers an action to perform when new metadata arrives in the stream.
An example of creating a JFR event stream might look like this. In this example, the stream checks for when a monitored process is reading from a file and prints to console the event's start time, end time, and total duration.
try (EventStream es = EventStream.openRepository()) {
es.onEvent("jdk.FileRead", event -> {
System.out.println("File read!");
System.out.println("Start: " + event.getStartTime());
System.out.println("End: " + event.getEndTime());
System.out.println("Duration: " + event.getDuration());
});
es.start();
}
A JFR event stream can be attached as both an in-process and an external process. Examples of how a JFR event stream can be attached to an external process include:
Java Agent
Process Id
JMX Socket
Read from a recording file
And more!
While JFR targets a <1% overhead with default settings, how a JFR Event Stream is implemented can impact performance. How many events are registered to the stream and the action(s) behavior will determine the performance impact for an in-process stream.
For external streams, how events are retrieved might impact performance. If events are streamed over a JMX socket, that will require the stream events to be serialized, which could mean significant overhead. However, if the external stream is reading from a recording file or directory, this will not put any additional overhead on the monitored process.
Happy coding!