Red Hat で Solution Architect として OpenJDK を担当している伊藤ちひろ(@chiroito)です。
この記事は、Red Hat Developerのブログ記事、Get started with JDK Flight Recorder in OpenJDK 8u | Red Hat Developer の翻訳記事です。
OpenJDK 8u 262 releaseには、いくつかのセキュリティ関連のパッチと、新しい追加機能であるJDK Flight Recorder(JFR)が含まれています。この記事では、OpenJDKの開発者向けに、JDK Mission Controlや関連ユーティリティーを使ったJDK Flight Recorderの使い方を紹介します。また、コンテナJFRとして知られるProject Hamburgについても簡単に紹介します。
JDK Flight Recorderについて
JDK Flight Recorder は、トラブルシューティング、モニタリング、プロファイリングを行うフレームワークで、Java 仮想マシン (JVM) コードに深く埋め込まれています。OpenJDK 11 で JEP 328 の一部として初めて導入されました。JDK Flight Recorderは、OpenJDK 11以前はJRockitにのみ商用機能として搭載されており、その後はJava Development Kit (JDK)のOracleディストリビューションに搭載されていました。JFRがOpenJDK 11で適切なオープンソースコンポーネントとしてリリースされて以来、Javaコミュニティのメンバーの中には、この機能を古いリリースでも利用できるようにしたいと考える人が増えてきました。
2019年、FOSDEMのJava DevRoomとOpenJDK Committers Workshopにおいて、OpenJDKコミッターのグループは、必要な変更や修正をOpenJDK 8uにバックポートすることを目標に、合同タスクフォースを結成することを決定しました。それから1年あまり、多くのパッチを経て、このプロジェクトは最終的にメインのアップストリームのOpenJDK 8u開発ツリーにマージされました。
最初に公開されたのは OpenJDK 8u 262 でしたが、自分で OpenJDK をコンパイルしようとすると、OpenJDK 8u 262 のデフォルトではコンパイル時に JFR をスキップすることがわかります。OpenJDK 8u 272 (10月予定)は、JFRをデフォルトでコンパイルする最初のリリースとなる予定です。
注意:Red Hat Enterprise Linux または Fedora を通じて OpenJDK を利用している開発者は、JFR のサポートを含む Red Hat Package Manager (RPM) ファイルを入手できます。皆様のご意見、特にバグや問題点などをお聞かせください。
JDK Flight Recorderの概要
JDK Flight Recorderは主に2つのコンポーネントで構成されています。1つはデータを含む重要な部分、もう1つはデータを記録し公開するための内部インフラです。このデータは、イベントと呼ばれる概念を介して抽象化されます。イベントには、様々な種類の有用な情報を関連付けることができ、時間内のサンプル、単一トリガーのイベント、または所定の時間を表すことができます。開発者は、メタデータやその他のコンテキスト情報をイベントの定義に追加し、その情報を使って分析ツールでイベントを説明したり、他の人間がイベントタイプをよりよく理解するために自己記述的にすることができます。例えば、ファイルアクセスが発生したときや、ガベージコレクション(GC)のコンパクションフェーズが始まったときに通知を受けたり、フルガベージコレクションのフェーズにかかった時間を知りたい場合があります。このようなイベントには、例えば、Period(期間)やFrequency(周波数)などのアノテーションが可能なフィールドが含まれている場合があり、JFRのツールを使用することで、分析時に特定の方法で視覚化することができます。OpenJDK 8uでは、160以上のイベントを記録し、分析できます。
これらのイベントは発生時に記録されますが、JFR自体はリアルタイムツールではなく、呼び出し時にイベントをストリームすることはありません。(後のOpenJDKにはJFR Event Streaming APIがありますが、その目的はイベントをリアルタイムでストリームすることではありません) 代わりに、基礎となるフレームワークは、スレッドローカルのバッファにイベントを保存し、それをグローバルなリングバッファに書き込みます。これらのバッファが一杯になると、周期的なスレッドによって最終的にディスクにフラッシュされます。これは、トランザクション・データベースで使用されるメカニズムに似たメカニズムを使用しています。
注意:JFRの設計は複雑に見えるかもしれませんが、メモリとCPUの両方を効率的に使用することができます。JFRのオーバーヘッドは一般的に約1%と非常に低く、ほとんどのケースではさらに低くなっています。オーバーヘッドが少ないということは、JFRを本番時に使用できる(している)ということです。これは、稼働コストが高くつく他の多くのソリューションとは異なります。
JFRの記録
JFRの記録ファイルは、すべてのイベントとそのメタデータをバイナリで表現したものです。この情報はチャンクに分けられています。チャンクとは、JFRの記録の中で、自己完結した情報の最小単位です。これは、別々に読んでも、チャンクに含まれるイベントを完全に説明できます。
すべての情報はLEB128エンコーディングの整数としてエンコードされています。これには、一定のプールポジションを参照する文字列も含まれます。このエンコーディングにより、各記録において高度なデータ圧縮が保証されます。また、GZip、LZMAとXZ、LZ4などの方法で記録をさらに圧縮できます。記録のディスクへの書き込みは、設定に応じて、要求時またはプログラムの終了時に行われます。また、一定期間ごとにディスクに書き込まれるエンドレスの録画も可能です。これにより、アプリケーションの時間経過に伴う動作を確認できます。つまり、JFRの設定オプションは柔軟なのです。
JDK Flight Recorderの使い方
デフォルトでは、OpenJDKのJDK Flight Recorderを制御するためのいくつかのメカニズムが用意されています。これにより、手元のユースケースに合わせるのが非常に簡単になります。最初の選択肢は、例えばJVMでJFRを直接起動することです。
$ java -XX:StartFlightRecording your.application.ClassName
コンマで区切ったオプションリストを使って、JFRをさらに設定できます。例として、終了時に記録をダンプできます。
$ java -XX:StartFlightRecording=dumponexit=true your.application.ClassName
アプリケーションの起動後にJFRを制御するには、お馴染みのjcmd
を使用します。
$ jcmd <pid> <pid>: The following commands are available: VM.unlock_commercial_features JFR.configure JFR.stop JFR.start JFR.dump JFR.check VM.native_memory ManagementAgent.stop ManagementAgent.start_local ManagementAgent.start VM.classloader_stats GC.rotate_log Thread.print GC.class_stats GC.class_histogram GC.heap_dump GC.finalizer_info GC.heap_info GC.run_finalization GC.run VM.uptime VM.dynlibs VM.flags VM.system_properties VM.command_line VM.version help For more information about a specific command use 'help <command>'.
直感的に言えば、jcmd
ユーティリティーを使うと、記録の開始と停止、記録の設定、記録の状態の確認、および録画のダンプができます。複数の記録を同時に実行することも可能です。
標準のJava Flight Recorder API(原文まま)を使用して、アプリケーションコードから直接記録にアクセスできます。このAPIを使用する際には、いくつかの点を理解する必要があります。そこで、このオプションについてさらに説明し、記事の最後に例を示します。
もうひとつの、間違いなくより便利な録画の取得方法は、JDK Mission Controlを介したものです。これは、記録の制御と分析のために特別に設計されたアプリケーションです。
注意:利用可能なjcmd
コマンドのリストの中にunlock_commercial_features
フラグがあることに気付いたかもしれません。JFRはOpenJDKの商用機能ではないことを認識しておくことが重要です。しかし、JDK 11以前のすべてのOracle JDKでは商用機能となっています。私たちは互換性の理由からこのフラグを残していますが、これは何もしませんので、安全に無視することができます。
JDK Mission ControlでJDK Flight Recorderを使う
OpenJDKには、jfr
というシンプルなツールがあります。これにより、JFRの記録を読み込んで、そこから有用なメトリクスを得られます。しかし、JFRレコーディングをJDK Mission Control (JMC)と組み合わせたときに、JFRレコーディングの本当の利点がわかるでしょう。JMCは、FedoraおよびRed Hat Enterprise Linux (RHEL) 7ではRed Hat Software Collections (RHSCL)経由で、RHEL 8ではモジュール経由で、Windowsユーザー向けにはOpenJDK developer portalからすでに入手可能です。JDK Mission Controlは、AdoptOpenJDKのようなダウンストリーム・ディストリビューションを通じても入手できます。
JMCの古いインストールを使用している場合、JDK Flight RecorderでOpenJDK 8uバージョンにアクセスしようとすると、警告ダイアログが表示されることがあります。これは、商用機能を使用しているかどうかを尋ねています。先に述べたように、OpenJDKでは(そしてOpenJDKのみでは)このメッセージを無視できます。このバグは、JDK Mission Controlの後のバージョンで修正されました。図1は、商用機能の警告を示しています。
図1:どのOpenJDKビルドでも、商用機能の警告を無視できます。
Demo: GC割り当てのプロファイリング
JDK Mission ControlプロジェクトのリーダーであるMarcus Hirt氏は、JFRとJMCを一緒に使うための素晴らしいチュートリアルのセットとデモを用意しました。新しいデモを作成するのではなく、このセクションで彼のコードを参照します。特に、GCの割り当て動作に関する彼の例、04_JFR_GC
を使って、データを自動的に分析し、改善点を提案するJMCの能力を紹介します。JMCの分析は、ルールエンジンと呼ばれる機能に基づいています。ルールエンジンは現在、JMC 8.0に向けて整備されており、分析のためのオプションを増やし、ツールを介して直接利用するためのより良いAPIを提供し、全体的なパフォーマンスを向上させることを目的としています。
GCデモは、単純に多くのデータを割り当て、それをMapに保存します。そして、割り当てサイクルごとにMapの内容をチェックします。実際のプログラムでは、このデータを使ってもっと面白いことをするでしょうが、このパターンはHashMapの非常に典型的な使用例を示しています。私たちの場合、このプログラムは問題なく動作しており、メモリ不足などのエラーも発生していません。そのため、このデモは隠れたパフォーマンス問題を探り、ボトルネックや最適化の可能性をチェックするための完璧な候補となります。
テンプレートの使用
JMC や JFR には、テンプレートという便利な機能があります。これにより、デフォルトの設定とイベントで録画を開始できます。これらのテンプレートは、メモリの検証やTLABに問題がある場合、jcmd
で記録を取得する際に、コマンドラインインターフェイス(CLI)で渡すことができる設定に対応しています。しかし、グラフィカル・ユーザー・インターフェース(GUI)を使えば、より簡単に設定内容を理解できます。この実験では、profilingテンプレートと、デフォルトの1分間の記録セッションを選択します。これで今回のデモには十分なデータが得られます。
図2に示すように、アプリケーションを実行して記録を取得すると、さらに調査しなくても、すぐに最適化したいことに対する直接的な答えを得られます。このアプリケーションでは、プリミティブからオブジェクトへの変換が非常に多く行われていますが、JMCではこれらの割り当てがどこで行われているかを教えてくれます。
図2:JDK Mission Controlの自動分析ビューでは、多くの問題が自動的に検出されます。
このデモは数年前のJava Oneでのハンズオンセッションのために作成されたもので、当時のJMCバージョンには高度な解析を行うオプションはありませんでした。セッションの参加者は、MemoryとTLABタブを探索して、図3に見られるような、より詳細なメモリ圧の表示を得ることを勧められました。
図3:TLABタブに表示されたメモリの割り当て状況。
注意:JMC 7以降のバージョンの解析ページは、より多くの情報を提供しており、さらに多くのルールや最適化戦略を提供しています。JMCは、HTMLページとしてエクスポートされるスタンドアロンのコンポーネントとして解析ページを提供しています。完全なIDEを使用せずに、JMCの分析をアプリケーションに簡単に統合できます。JFR APIと一緒に使用すると、JMCのスタンドアロン解析コンポーネントは、メモリオーバーヘッドを非常に低く抑えながら、インフラストラクチャに堅牢なモニタリングとプロファイリングソリューションを統合できます。
JFRのOld Object Sample Eventによるメモリプロファイリング
従来、効果的なメモリプロファイリングを行うためには、GCルートやアロケーションの履歴を確認するために、時間をかけて完全なヒープダンプにアクセスして調査する必要がありました。また、同様にコストのかかるオプションとして、Java Native Interface (JNI)を介してオブジェクトの割り当てを抽出するエージェントのような方法を使用できます。しかし、完全なヒープダンプに含まれる機密情報を考えると、それは必ずしも可能ではありません。JFRは、OpenJDK 8uにバックポートされたOld Object Sample Eventにより、ここでも助けになります。
注釈:JFRのOld Object Sample Eventについてもっと知りたいという方には、Marcus Hirt氏のブログ記事を改めてご紹介します。彼のブログをぜひチェックしてみてください。プロファイリング、JMC、JFRについての情報、技、詳細など、信じられないほどの情報源です。
ここでは、JFRのOld Object Sample Eventを探るために、別の小さな自己完結型の例を使います。これは、コードを読めば一目瞭然の例ですが、私たちの選択肢を探るにはとても良いものです。
public class Leaks { private static final Map<Object, Object> SESSION_DATA = new HashMap<>(); public static class UserInformation { private byte[] data = new byte[10000]; } public static void main(String[] args) { String userId = "user"; while (true) { UserInformation user = (UserInformation) SESSION_DATA.get(userId); if (user == null) { user = findUserInformation(userId); // SESSION_DATA.put(userId, user); // 正しい SESSION_DATA.put(user, user); // 誤り } sleep(); } } private static UserInformation findUserInformation(String userId) { sleep(); return new UserInformation(); } private static void sleep() { try { Thread.sleep(1); } catch (InterruptedException e) {} } }
このコードで強調されているミスは、本番環境に移行する前に迅速なテストで発見されるようなエラーですが、例として、本番環境に入り込んでしまったコードのバグであると仮定しましょう。図4は、Old Object Sample EventのプロファイリングをオンにしたJFRセッションを示しています。(このセッションでは、ヒープダンプは害を及ぼされていません。)
図4:Old Object Sample Eventが自動分析ビューに表示されています。
その分析結果は、どこを見れば良いかを瞬時に教えてくれます。HashMapが何度も何度も埋められ、オブジェクトの数が増えているだけでなく、メモリの割り当ても多くなっています。コードを読まなくても、このMapはあまり制御されずにループでオブジェクトを埋めていることが予想されます。図5に示すように、Memoryタブではさらに多くのことがわかります。
図5:Live Objectページとアプリケーションコードを並べて表示したもの。行番号が一致していることに注目。
このタブでは、プログラムコードとLive Objectページを並べて表示しています。行番号の付いたスタックトレースは、どこに問題があるかを正確に示しています。
プロファイリングについての注意点
オブジェクトの割り当てと保持を追跡できることは、メモリ問題を分析する際に最も重要なツールの1つです。あるバグを思い出しました。これは、作成されたオブジェクトの数が膨大であったため、修正が困難でした。しかし、これはリモートのX11接続を介してアプリケーションのUIを実行しているときだけです。さらに、ユーザーがマウスを動かしたり、ボタンをクリックしたりするたびに、オブジェクトが作成されていました。これにより、多くのメソッドがグラフィカル・インターフェースの位置を再計算しています。しかし、これは一部のケースに限られます。
この2つの動作がリンクしているのは、リモート接続の処理にバグがあったからです。位置を計算するには、画面上のオブジェクトの相対的な位置を知る必要がありました。リモート接続であるため、多数のX11アトムというものが作成され、回線上を行き来していました。ユーザーが複数のアプリケーションを実行している場合は、さらに多くのトラフィックが発生します。Javaコードはこれらのアトムを傍受することになります。これは、Javaの表現を作り、さらに計算をして、それを繰り返しているのです。
当時はJMCではなく、Thermostatという似たようなツールにアクセスしていました。Bytemanとの統合を利用して、それらのオブジェクトがどこで作成されたのか、また、それらのオブジェクトを作成するに至ったコードパスがなぜ異なる形で実行されたのかを分析するスクリプトを作成しました。通常のメソッドプロファイラでは、結果が集約されてしまうため、このようなことは困難です。このような情報がJFRの記録から直接得られることは非常に重要であり、時間の節約にもなりました。さらに言えば、お客様が導入時に記録したものを送るだけで良いのです。ローカルでエラーを再現したり、ツールを追加インストールしたり、ポートを開いたり、エージェントを起動したりする必要はありません。この場合、必要なのは記録だけです。
JDK Flight Recorder API
先ほど、JFRには内部APIが付属していると述べました。このAPIは、jdk.jfr
名前空間の下に存在し、記録を管理したり、アプリケーション用のカスタムイベントを作成したりするためのクラスを含んでいます。
最も簡単に書けるプログラムは、JFRが利用可能かどうかをチェックするものです。
public class CheckJFR { public static void main(String[] args) { boolean isAvailable = FlightRecorder.isAvailable(); System.err.println(isAvailable); } }
そして、APIを使って、アプリケーションからプログラム的に記録を開始したり停止したりできます。例えば、以下のクラスは、JFRマネージャーを作成するための抽象化されたクラスです。
import java.io.File; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import jdk.jfr.Configuration; import jdk.jfr.Recording; public class LocalJFR { private Map<Long, Recording> recordings = new HashMap<>(); @Override public long startRecording(String configName) throws Exception { Configuration c = Configuration.getConfiguration(configName); return startRecording(new Recording(c), "jfr-recording"); } @Override public long startRecording(String configName, String recordingName) throws Exception { Configuration c = Configuration.getConfiguration(configName); return startRecording(new Recording(c), recordingName); } @Override public long startRecording() throws Exception { return startRecording(new Recording(), "jfr-reopenjdk-jfr-2cording"); } public long startRecording(Recording recording, String name) throws Exception { long id = recording.getId(); Path destination = File.createTempFile(name + "-" + id, ".jfr").toPath(); recording.setDestination(destination); recordings.put(id, recording); recording.start(); return id; } public File endRecording(long id) throws Exception { Recording recording = recordings.remove(id); recording.stop(); recording.close(); return recording.getDestination().toFile(); } }
これは定義できる最もシンプルなイベントです。
@Label("Basic Event") @Description("An event with just a message as payload") public class BasicEvent extends Event { @Label("Message") public String message; }
プログラムによるJFRイベントの作成と監視
OpenJDKにおけるJFRの作者の一人であるEric Gahlin氏は、JFR APIを使用したデモや小規模なテストの包括的な一覧をまとめました。このAPIは、OpenJDK 11以降のJava仕様に含まれていますが、OpenJDK 8では仕様に含まれていないため、すべてのOpenJDKの実装で利用できるわけではありません。
バージョン間の移植や移行を容易にするために、空の実装を持つシンプルなcompat-jfrを作成しました。このパッケージを使うと、ユーザーはコードを計測したり、カスタムイベントを作成したり、APIを使って記録を管理できます。しかし、実装は空なので、メソッドは何もせず、イベントはメモリやディスクに書込まれず、問い合せしてもJFRは利用できないと報告し、起動できません。アプリケーションは正しく機能し、コンパイルされ、実行されるので、互換性の面でも優れています。compact-jfrは、コマンドラインで依存関係にあるものとして使用するか、JDKのjre/lib/ext
ディレクトリに追加して使用できます。
イベントAPIを使ってカスタムイベントを作成するだけでなく、実行中のアプリケーションにイベントを追加するとコードを計測できます。JMCには、Agentという名前の便利なツールがあります。JMC Agentは、一連の設定を使用してイベントを定義し、実行中のコードでイベントを計測します。セッションが終了すると、計測は削除されます。Bytemanに精通している方(そうであるべきですが)、Agentは非常に似ていますが、自由に使えるチューリング完全な言語の代わりに、AgentはJFRイベントのみに焦点を当てています。スコープを縮小したことで、JFRをより細かいツールで計測するという問題に特化でき、セキュリティやパーミッションなどの問題も部分的に解決できます。また、Agentを制御・設定するためのJMCプラグインにも取り組んでいます。これは現在進行中ですが、すでに便利な機能を備えており、こちらでご覧いただけます。
コンテナでJFRを使う (Project Hamburg)
この記事で紹介したツールはすべて、特定のデプロイメントに合わせてJDK Flight Recorderを微調整できるという点で素晴らしいものです。しかし、コンテナ内でJFRを使用する開発者には、まだかなりの作業が必要であることに気づきました。まず第一に、JFRの受信側には、Java Management Extensions(JMX)を介した接続が必要です。この接続はもちろん安全にできます(すべきです)が、Red Hat OpenShift Container Platform (OCP)のようなコンテナプラットフォームでは、内部のポートを外部に開放することを禁止したり、困難にしたりできます。また、複数のプロセスを一度に追跡するには、上位のツールを使用しないと複雑です。OpenShift には、この作業を支援するデプロイメントコンソールがありますが、より一般的なソリューションがまだ必要です。
このため、私たちはContainer JFRというプロジェクトを作成し、Project Hamburgとも呼ばれています。Container JFRは、シンプルな3層構造のアプリケーションです。これは、コンテナ内でJMXを介して様々なアプリケーションに接続し、外部にWebサービスのインターフェースを公開するコントローラエージェントを含んでいます。JMX接続はコンテナ内に隠すことができ、コンテナ以外の世界でも、つまりファイアウォールの後ろに置くことができます。一方、Webサービス・インターフェースは認証によって保護されています。このインターフェースでは、同じエンドポイントから複数のJVMを制御できるため、複数のデプロイメントに最適です。
もう一つのコンポーネントは、Webサービスを利用したWeb UIです。管理をシンプルにするだけでなく、何よりもJMCの自動分析機能を統合することで、アプリケーションのパフォーマンスをすぐに見ることができ、分析によって特定の問題が指摘された場合にのみダウンロードする記録を決定できます。このプロジェクトには、ブラウザ上でグラフを作成できるGrafanaデータソースも含まれています(例えば、ユーザーがダッシュボードにレコーディングを統合できるようになります)。実験的なPrometheusエクスポーター(記録を処理するには最適な方法ではありませんが、それでも便利です)、そして最後に、OpenShiftやKubernetesのための包括的なOperator APIのセットがあります。これらのOperator APIを使用することで、マウスをクリックするだけでプロジェクトのインストール、実行、設定を行えます。
注:Gunnar Morlingがカスタム、アプリケーション固有のJFRイベントを使用してREST APIを監視に関する包括的なブログ記事を書いています。この投稿では、ストリーミングAPIとカスタムJFRイベントが説明されているので、詳細はそちらを参照してください。Gunnarは最高です!
結論
JDK Flight Recorderは、ランタイムシステムに負担をかけることなく、このような高レベルの情報を公開できるOpenJDKで利用可能な初めてのモニタリングおよびプロファイリングツールです。JFRは、JVMに深く統合されているため、そのようなレベルの情報を提供します。イベントAPIやエージェント・ツールを使ってカスタム・イベントを作成できるので、ランタイムからだけでなく、アプリケーションの観点からもJFRを活用できます。
OpenJDKは大規模なコードの公開に貢献しましたが、JDK Flight RecorderはOpenJDKがオープンソース化されて以来、最も重要な貢献をしたと言えるでしょう。Oracle社がJDK Flight RecorderとJDK Mission Controlをオープンソース化した際には、Javaコミュニティに多大な貢献をしたことが認められるべきでしょう。今回のOpenJDK 8uへのバックポートにより、このインフラが、積極的にメンテナンスされているすべてのバージョンのOpenJDKにようやく導入されました。
私たちは、お客様がOpenJDKの新しいバージョンに移行して、すべての追加機能とパフォーマンスの改善の恩恵を受けることを望んでいます。JFRをツールボックスに追加することで、どのバージョンのOpenJDKでも、アプリケーションがより良く、より速く、より問題なく動作するようになります。
謝辞
お礼を申し上げたいと思います。
- Marcus Hirt氏には、JDK Mission Controlプロジェクトのプロジェクトリーダーを務めていただきました。彼は、コミュニティへの貢献という点で、常に高い水準を保っています。また、彼のブログはインスピレーションと知識の素晴らしい源となっています。
- Gunnar Morlingは、Container JFRの開発初期に協力してテストを行い、フィードバックや提案をしてくれました。 Red HatのJDK Mission Controlチームは、JMCへの素晴らしい貢献と、Agent、JFR Compact、Container JFRへの取り組みをしてくれました。
- 最後に、この素晴らしい技術を提供してくれたオリジナルのJDK Flight Recorderチームと、それをオープンソース化してくれたOracle社に感謝します。素晴らしいといえば、2020年にJMCがbest Java Featureコンテストで優勝したことをご存知でしょうか?
その他のリソース
ここでは、この記事で紹介したリソースと、JDK Flight RecorderとJDK Mission Controlの詳細を知るために利用できるプレゼンテーション、記事、ソースコードへの興味深い追加リンクを紹介します。
- An introduction to middleware application monitoring with Java Mission Control and Flight Recorder (FOSDEM presentation, 2019)
- JMC & JFR—2020 vision (FOSDEM presentation, 2020)
- More about the JFR compatibility API for OpenJDK 8
- Low overhead method profiling with Java Mission Control (Marcus Hirt, 2013)
- Compressing flight recordings (Marcus Hirt, 2019)
- Using Java Flight Recorder with OpenJDK 11 (Laszlo Csontos, 2018)
- More about the Container JFR project
- More about JDK Mission Control
- Source code and examples for understanding how to create and use custom events with JDK Flight Recorder (Gunnar Morling, 2020)
- Flight Recorder samples: Code snippets illustrating how to use the JDK Flight Recorder API
- jmc-jshell: An easier way to experiment with the JDK Flight Recorder and the JMC core classes
- Introduction to JmFrX: a small utility to capture JMX data with JDK Flight Recorder (Gunnar Morling, 2020)