#chiroito ’s blog

Java を中心とした趣味の技術について

JFR Event Streamingを軽い気持ちで非同期にしてみたら動かなかった

もともとこんな感じで同期で作っていたので「es.start()の部分をes.startAsync()にしたら非同期になるだろ」と思いやったらイベントが全然処理されない。

import jdk.jfr.Configuration;
import jdk.jfr.consumer.EventStream;
import jdk.jfr.consumer.RecordingStream;

public class JfrEventStreamingInProcessAsync {
    public static void main(String[] args) throws Exception {

        Configuration config = Configuration.getConfiguration("default");

        try (EventStream es = new RecordingStream(config)) {

            es.onEvent("jdk.CPULoad", System.out::println);
            es.start();
        }
    }
}

デバッグ実行で調べてみると、JFR Event Streamng の非同期実行が始まる前に、非同期実行とストリーミング用のJFRがなぜか止まってしまい処理できないようだ。 JFR Event Streamingスレッドがclose()を呼ぶ前にステータスがcloseされてしまうので、別のスレッドがclose()を実行しているに違いない。 どうやら、EventStreamをtry-catch-resoucesで作っていたのでAutoClosableによりmainスレッドがclose()を実行し、その後立ち上がってくるJFR Event Streamingスレッドが処理をする頃にはJFRが停止させられてしまっていた。

なのでtry-catch-resourcesをやめたのと、アプリケーションが終了する時にclose()メソッドが呼ばれるようにした。Quarkus での実装例は以下のようにApplicationScopedを使用しました。ただのJavaアプリで実装する時は、onStartの部分をpreMainで、onStopの部分をシャットダウンフックで実装すれば良いと思います。

@ApplicationScoped
public class JfrEventStreamingApplicationBean {

    private EventStream es;

    void onStart(@Observes StartupEvent ev) {
        try {
            Configuration config = Configuration.getConfiguration("default");

            this.es = new RecordingStream(config);

            this.es.onEvent("jdk.CPULoad", System.out::println);

            this.es.startAsync();
        } catch (ParseException | IOException e) {
            System.err.println("Couldn't start JFR Event Streaming");
        }
    }

    void onStop(@Observes ShutdownEvent ev) {
        if (this.es != null) {
            es.close();
        }
    }
}