【Java学習|豆知識】Javaのパフォーマンス分析の要「JFR (Java Flight Recorder)」を徹底活用する

導入

システム運用において、本番環境で発生する「突発的な遅延」や「原因不明のメモリリーク」に悩まされた経験はありませんか?デバッガを繋げない環境や、再現性の低い問題に対して強力な武器となるのが「JFR (Java Flight Recorder)」です。JFRはJVM内部で発生するイベントを記録する低オーバーヘッドなプロファイリングツールであり、パフォーマンス診断の必須スキルと言えます。

基礎知識

JFRはJVMの内部に組み込まれたイベントレコーディングエンジンです。最大の特徴は、オーバーヘッドが極めて小さい(通常1%未満)ことです。そのため、プロダクション環境で常時稼働させていてもシステムへの影響がほとんどありません。

JFRが記録するのは以下のような情報です。
・CPU利用率やメモリのGC(ガベージコレクション)状況
・スレッドのロック競合や待ち時間
・JITコンパイルの統計情報
・クラスロードの発生タイミング
これらは「イベント」として記録され、`.jfr`ファイルとして保存されます。これをJDKに同梱されている「JDK Mission Control (JMC)」で可視化することで、システムのボトルネックを視覚的に特定できます。

実装/解決策

JFRを活用する最も簡単な方法は、アプリケーション起動時にコマンドライン引数を付与することです。これにより、トラブル発生時に「過去に何が起きていたか」を遡って調査可能になります。

サンプルプログラム

以下のコマンドは、アプリケーション起動時にJFRを有効にし、メモリ状況とスレッドの競合を詳細に記録する例です。


java -XX:StartFlightRecording=filename=recording.jfr,duration=60s,settings=profile -jar my-application.jar

また、プログラムの実行中に特定の処理だけを記録したい場合は、以下のコードのように明示的に記録を開始・終了することも可能です。

import jdk.jfr.Recording;

public class JfrSample {
public static void main(String[] args) throws Exception {
// 記録の開始
try (Recording recording = new Recording()) {
recording.setName(“PerformanceAnalysis”);
recording.start();

// 診断したい重い処理
performHeavyTask();

// 記録の終了と保存
recording.stop();
recording.dump(java.nio.file.Paths.get(“my-task.jfr”));
System.out.println(“記録が保存されました。”);
}
}

private static void performHeavyTask() {
// ここに負荷のかかる処理や調査対象のロジックを記述
try { Thread.sleep(1000); } catch (InterruptedException e) {}
}
}

応用・注意点

現場でJFRを運用する際、以下の点に注意してください。

1. 設定ファイルの使い分け
JFRには `default` と `profile` という二つの設定があります。`default`は軽量ですが、より詳細な分析(メソッドサンプリングなど)が必要な場合は `profile` を選択してください。ただし、`profile`は多少オーバーヘッドが増加します。

2. ディスク容量の管理
常時記録を行う場合は、`maxage`(保存期間)や `maxsize`(最大サイズ)を指定してください。これを怠ると、ディスクを圧迫してシステムダウンを招くリスクがあります。
例: `-XX:StartFlightRecording=filename=app.jfr,maxage=1d,maxsize=2GB`

3. JITやGCとの関連性
JFRはJITコンパイラの最適化状況や、G1/ZGCの停止時間も詳細に記録します。特にZGCなどの低レイテンシGCを使用している場合、JFRのイベントログを確認することで、GCの停止時間がアプリケーションの応答時間にどれだけ寄与しているかを定量的に判断できます。

JFRは単なるログツールではなく、JVMの「健康診断レポート」です。ぜひ本番環境でのトラブルシューティングに役立ててください。

コメント

タイトルとURLをコピーしました