Introduction to the

Java Flight Recorder (JFR) is a diagnostic and performance analysis tool for the JVM. It collects data about the JVM and the Java applications running on it. JFR is integrated into the JVM, so JFR has very little impact on JVM performance and can be used with confidence.

Generally speaking, the performance impact is less than 1% when using the default configuration.

JFR has a long history. It’s been around since Oracle acquired BEA in 2008. JFR generally works in conjunction with JMC (Java Mission Control).

JFR is an event-based, low-overhead analysis engine with a high-performance back end that can write events in binary format, while JMC is a GUI tool for examining data files created by JFR.

These tools first appeared in BEA’s JRockit JVM and were eventually ported to the Oracle JDK. JFR was originally a commercial version, but in JDK11, JFR and JMC were fully open source, which meant we could use them in non-commercial situations.

In JDK 14 today, a new JFR feature called JFR Event Streaming was introduced, which we’ll cover in detail in this article.

Let’s start with JFR and JMC.

For more, visit www.flydean.com

JFR

Above we briefly introduced JFR. JFR is a tuning tool for the JVM that continuously collects events in the JVM and Java applications to provide data for subsequent JMC analysis.

An Event consists of three parts: a timestamp, an Event name, and data. JFR also handles three types of events: events that last for a period of time, events that are triggered immediately, and events that are sampled.

To ensure the latest impact on performance, select the event type you need when using JFR.

JFR collects events from the JVM, writes them to a small Thread-local cache, flusher them to a global in-memory cache, and writes the cached data to disk.

Or you can configure JFR not to write to disk, but only some events will be stored in the cache. This is why there is JDK14 JEP 349.

There are many ways to start JFR, and we’ll focus on the following two:

  1. Add command line arguments
-XX:StartFlightRecording:<options>
Copy the code

The format of the start command line arguments is described above.

JFR can retrieve over a hundred different types of metadata. If we had to specify this metadata individually, it would be a very large capability. So the JDK already provides us with two default profiles: default.jfc and profile.jfc.

Default. JFC is the default record level, which has little impact on JVM performance and is suitable for ordinary, most applications. Profile.jfc contains more detail and has more impact on performance.

If you don’t want to use the default two JFC files, you can create your own.

Let’s look at a more complete command-line argument:

-XX:StartFlightRecording:disk=true,filename=/tmp/customer.jfr,maxage=5h,settings=profile
Copy the code

The command above creates a profile information file with a maximum age of 5h.

  1. Using JCMD

It is still too cumbersome to add arguments on the command line. If we want to add JFR dynamically, we can use the JCMD command.

jcmd <pid> JFR.start name=custProfile settings=default
jcmd <pid> JFR.dump filename=custProfile.jfr
jcmd <pid> JFR.stop
Copy the code

The above command starts JFR in a running JVM and dumps the statistics to a file.

The above custprofile.jFR is a binary file, and in order to analyze it, we need the tool JMC that goes with JFR.

JMC

JDK Mission Control is a suite of tools for managing, monitoring, profiling, and troubleshooting Java applications.

In JDK14, JMC is released separately from the JDK. We can download it and install it.

Let’s start with a program to test JFR.

@Slf4j
public class ThreadTest {

    public static void main(String[] args) {
        ExecutorService executorService= Executors.newFixedThreadPool(10);
        Runnable runnable= ()->{
            while(true){
                log.info(Thread.currentThread().getName());
                try {
                    Thread.sleep(500);
                } catch(InterruptedException e) { log.error(e.getMessage(),e); }}};for(int i=0; i<10; i++){ executorService.submit(runnable); }}}Copy the code

It’s a very simple program that starts 10 threads, so let’s start this program.

Then take a look at the JMC interface:

You can see the ThreadTest program running on the native machine on the left side of the interface.

Click on MBean server, you can see the Panel information of the Java program, which contains CPU, stack information.

There are seven tabs below for Overview, MBean browser, Trigger, System, Memory, Thread, and diagnostic commands.

The following TAB lets you get more detailed information about the Java program, and through triggers and diagnostic commands, you can also send commands to the JVM of the target Java program.

JMC is very powerful and has a lot of functions, you can run the details to experience.

Since this article focuses on JFR, let’s look at how to create and analyze JFR in JMC.

Create JFR

Below the MBean server on the right above is the flight recorder, which is our target.

Click on the flight recorder:

We are ready to start creating a JFR.

The object file is the generation address of JFR, the name can be arbitrarily chosen, and the record time indicates how long the JFR needs to be recorded.

Click next:

This step allows you to select more detailed JVM parameters.

Click next:

Here, we can select the Profile event options that we want to monitor. You can choose according to your needs.

The last click completes the JFR creation.

Analysis JFR

Above our JFR recorded the Profile for 1 minute, after 1 minute we can see the target JFR file generated.

After the JFR is generated, JMC automatically opens the generated JFR file and we get an outline view.

It contains Java applications, JVM internals, environments, and event browsers.

The event browser lists the events that we monitored within a minute.

The JMC browser can monitor not only native applications, but also remote applications. Since JMC is connected through the JMX protocol, remote Java programs need to enable JMX protocol support.

JFR event

JMC works well, but listening to JFR files one by one can be tedious. Let’s take a look at how to write code to listen for JFR events.

Again, if we want to programmatically retrieve “Class Loading Statistics”, we can do so.

On the right side of the image is the detailed information, which contains three main fields: start time, Loaded Class Count, and Unloaded Class Count.

Our idea is to use the JDK. JFR. Consumer. RecordingFile to read JFR generated file, and then to parse data in a file.

The corresponding code is as follows:

@Slf4j
public class JFREvent {

    private static Predicate<RecordedEvent> testMaker(String s) {
        return e -> e.getEventType().getName().startsWith(s);
    }

    private static final Map<Predicate<RecordedEvent>,
            Function<RecordedEvent, Map<String, String>>> mappers =
            Map.of(testMaker("jdk.ClassLoadingStatistics"),
                    ev -> Map.of("start".""+ ev.getStartTime(),
                            "Loaded Class Count".""+ ev.getLong("loadedClassCount"),
                            "Unloaded Class Count".""+ ev.getLong("unloadedClassCount")));@Test
    public void readJFRFile(a) throws IOException {
        RecordingFile recordingFile = new RecordingFile(Paths.get("/Users/flydean/flight_recording_1401comflydeaneventstreamThreadTest21710.jfr"));
        while (recordingFile.hasMoreEvents()) {
            var event = recordingFile.readEvent();
            if(event ! =null) {
                var details = convertEvent(event);
                if (details == null) {
                    / / the details is empty
                } else {
                    // Print the target
                    log.info("{}",details); }}}}public Map<String, String> convertEvent(final RecordedEvent e) {
        for (var ent : mappers.entrySet()) {
            if (ent.getKey().test(e)) {
                returnent.getValue().apply(e); }}return null; }}Copy the code

Notice that in the convertEvent method, we convert the Event read from the file into a Map object.

In building the map, let’s figure out whether the name of the Event. We need the JDK ClassLoadingStatistics, then transfer to other fields on the Event. Finally output.

Running results:

{start=2020-04-29T02:18:41.770618136Z, Loaded Class Count=2861, Unloaded Class Count=0}...Copy the code

You can see that the output is the same as above.

JFR event flow

With all that said, we finally get to our topic for today: JFR event flow.

In the above JFR event, we need to read the JFR file and analyze it. But files are dead, people are alive, and every analysis requires a JFR file is simply too complicated. Even a programmer can’t stand it.

In the JFR Event flow, we can listen for changes in events to be processed in the program accordingly. This allows you to listen for event changes without generating a JFR file.

    public static void main(String[] args) throws IOException, ParseException {
        // Two default profiling configuration files
        Configuration config = Configuration.getConfiguration("default");
        try (var es = new RecordingStream(config)) {
            es.onEvent("jdk.GarbageCollection", System.out::println);
            es.onEvent("jdk.CPULoad", System.out::println);
            es.onEvent("jdk.JVMInformation", System.out::println);
            es.setMaxAge(Duration.ofSeconds(10)); es.start(); }}Copy the code

Look at the example above. We through the Configuration. GetConfiguration (” default “) get the default Configuration by default.

Then build the default RecordingStream. With the onEvent method, we process the corresponding Event.

conclusion

This article covers JFR Event Stream, the latest feature of JFR, JMC, and JDK14. I hope I can help you in your work.

Examples of this article github.com/ddean2009/l…

Author: Flydean program stuff

Link to this article: www.flydean.com/jdk14-jfr-j…

Source: Flydean’s blog

Welcome to pay attention to my public number: procedures those things, more wonderful waiting for you!