JMCエージェントによるJVM性能監視

Red Hat で Java Platform Advocate として OpenJDK を担当している伊藤ちひろ(@chiroito)です。

この記事は、Red Hat Developerのブログ記事、JVM performance monitoring with JMC agent | Red Hat Developer の翻訳記事です。


https://developers.redhat.com/sites/default/files/styles/article_feature/public/blog/2016/06/openjdk.png?itok=jZRi5lJl

最近のJDK Mission Control (JMC) 8.1リリースに伴い、新しいJMCエージェントプラグインに目を向ける良い機会です。アップストリームにマージされたこのプラグインは、Javaアプリケーションを再起動または再構築することなく、実行中のJava仮想マシン(JVM)にカスタムJDK Flight Recorder(JFR)イベントを追加する便利な方法を提供します。

JDK Mission Control と JDK Flight Recorder

本番環境におけるJavaアプリケーションの性能監視にはコストがかかりますが、監視機能をインストールするためにJVMを再起動できない、あるいは望ましくない環境ではなおさらのことです。JDK Flight Recorder (JFR) は、JVMに組み込まれたJavaアプリケーション監視機能で、性能の負担や 設定を最小限に抑えます。

注意:コンテナ化されたJVMからフライトレコーディングを取得、保存、分析できる必要があるのでしょうか?Cryostat 2.0を発表:コンテナ用JDK Flight Recorder - 赤帽エンジニアブログ をチェックしてみてください。

JMC エージェント

JMCエージェントは、JDK Mission Controlとともに開発されているツールで、JVMを再起動することなく、実行時にカスタムJDK Flight Recorderイベントを追加できます。このエージェントは、カスタムJDK Flight Recorderイベントのバイトコードと、指定されたメソッド内でイベントを発生させるバイトコードを追加することで機能します。また、JMCエージェントは、メソッド・パラメータやクラス・フィールドの値を取得し、カスタム・イベントの一部として発行できます。JDK Mission Control 8.1には、エージェントのライフサイクルを通じて制御するためのプラグインが含まれています。

Limitations of JMC agent

JMCエージェントには,開発者が知っておくべきいくつかの制約があります.

  • Synthetic Classでは動作しません。
  • また、入れ子のプライベート・フィールドにアクセスできません。
  • 新たにアップロードされた設定は、システム・クラス・ローダで定義されたクラスでのみ動作します。

JMCエージェントプラグインの入手方法

JMCエージェントプラグインは、アップストリームJDK Mission Control 8.1に含まれているため、ダウンロードして実行するだけで利用を開始できます。エージェントプラグインは、JMCエージェントJARも必要となります。JDK Mission Control 8.1.0 と JMC agent JAR は、Eclipse Adoptium Working Group の jmc-overrides GitHub ページからダウンロードできます。

JMCエージェントプラグインの使用

JMCエージェントプラグインの使い方を説明するために、Webページから単語を読み込んで解析し、データベースに格納するプログラムを考えてみましょう。以下に、いくつかのメソッドのサンプルを示します。

private static void loadWords(String path) {
  try {
    List<String> lines = retrieveWords(path);

    for (String line : lines) {
      for (String word : Arrays.asList(line.split(" "))) {
        doWork(word);
        storeWord(word);
      }
    }
  } catch (Exception e) {
    e.printStackTrace();
    System.err.println(e.getMessage());
  }
}

private static void storeWord(String word) {
  Jedis jedis = pool.getResource();
  jedis.sadd(key, word);
  pool.returnResource(jedis);
}

この記事では、storeWord メソッドにカスタムイベントを組み込み、そのパラメータとして渡された単語を取得したいと思います。エージェントなしでこれを行うには、独自のカスタムイベントクラスを作成し、そのクラスを関数へ発行するコードを追加し、アプリケーションを再コンパイルして再起動する必要があります。実稼働環境では、これはコストがかかり、実現しにくいでしょう。しかし、エージェントを使用すれば、最小限の介入でこれを行えます。

JDK Mission Controlを起動すると、JVMブラウザの下にエージェントプラグインが利用可能になります。図1に示すように、調査したいJVMを見つけ、エージェント・オプションを選択するだけです。エージェントのJARファイルと、読み込む対象の探査定義が記述されたオプションのXMLファイルの入力を要求されます。エージェントプラグインには、作成プロセスを導くウィザードがあり、手動でXMLを記述する必要がないため、このXMLファイルはオプションとなります。

https://developers.redhat.com/sites/default/files/styles/article_full_width_1440px_w/public/browser.png?itok=xWPpobrJ

図 1. JVMブラウザは、エージェントJARとオプションのXML構成を提供するページを提供

エージェント JAR を与えると、図 2 に示すようなエージェントプラグインの表示画面が開きます。このページには、現在有効な取得イベントと、現在有効なエージェントの構成が表示されます。

https://developers.redhat.com/sites/default/files/styles/article_full_width_1440px_w/public/landing.png?itok=uFjrNOPs

図 2. エージェントプラグインの表示画面(現在は空欄)には、設定とイベントが一覧表示

このページでは、ページ右上の保存アイコンを使って、現在有効な探査と設定を保存し、後で使用できます。また、読み込みアイコンを使って設定を読み込めます。

事前マネージャでJMCエージェントを設定する

事前設定を作成するもう一つの方法は、ウィンドウメニューの下にある事前設定マネージャを使用することです。この事前設定マネージャーでは、既存の設定から新しい事前設定を作成できるだけでなく、そのプロセスを導くためのウィザードのセットから新しい事前設定を作成できます。

事前設定マネージャーを使用するときに表示される最初のページは、図3に示すエージェントの設定オプションです。これらのオプションを使用して、生成されたイベントクラスに使用される接頭辞を定義し、さらにエージェントが toString メソッドを呼び出したりコンバータを使用したりすることを許可するかどうかを設定できます。これらは高度な機能で、メソッドパラメータ、戻り値、フィールドの取り込みを可能にします。

https://developers.redhat.com/sites/default/files/styles/article_full_width_1440px_w/public/global.png?itok=lOurqjap

図 3. Edit Preset Global Configurations ページは、すべてのイベントの高度な機能の使用を制御

カスタムイベントの定義

ここから、追加したいカスタムイベントの定義を開始します。図 4 に示すように、カスタムイベントの名前と説明、およびそのイベントを組み込むクラスとメソッドを定義することから始めます。クラス名には完全修飾名を、組み込み対象のメソッドにはメソッド記述子を指定します。

https://developers.redhat.com/sites/default/files/styles/article_full_width_1440px_w/public/event_0.png?itok=56GZXJoW

図 4. 特定のイベントを設定するには、「Edit Event Configurations」ページを使用

メソッド記述子は、(Parameter Descriptor*)Return Descriptorの形式をとります。例えば、void storeWord(String word)というメソッドの場合、メソッド記述子は、(Ljava/lang/String;)Vとなります。メソッド記述子の形式についての詳細は、JVMの仕様書を参照してください。

その他のオプションとして、スタックトレースや例外を記録するかどうか、またイベントを発生させる場所 (メソッドの先頭や最後など) を指定できます。今回は、開始位置でイベントを発行することにします。

イベントのパラメータやフィールドを取得

このページの後には、図 5 に示すように、パラメータを取得するためのウィザードと、取得したメソッドの値や取得したクラス内のフィールドを返すためのウィザードが追加されています。興味のあるパラメータを取得するには、パラメータの位置とそれが戻り値であるかどうかを指定する必要があります。この場合、最初の、そして唯一のパラメータなので、それは0番目の値を持ちます。

https://developers.redhat.com/sites/default/files/styles/article_full_width_1440px_w/public/parameter.png?itok=lER5fWux

図5. メソッドの特定の部分へのアクセスを制御するには、「Edit a Parameter or Return Value」ページを使用

イベントの作成とパラメータやフィールドの取得が完了すると、エージェントプラグインは、図6に示すように、完成したXML構成の確認をします。完了したら、設定をターゲットJVMに読み込んで、JDK Mission Controlを使用して、新しく作成されたイベントによるフライトレコーディングを開始できます。

https://developers.redhat.com/sites/default/files/styles/article_full_width_1440px_w/public/preview.png?itok=3cxrQqx0

図 6. Preview Preset Output ページには、あなたの仕様から自動的に生成された XML 設定が表示

事前設定が完了し、読み込まれると、図7に示すように、エージェントプラグインのステータスページにイベントと設定オプションが表示されます。

https://developers.redhat.com/sites/default/files/styles/article_floated/public/status.png?itok=HLQ1mtlH

図7. エージェントプラグインのステータスページには、設定したパラメータが表示

まとめ

JDK Flight Recorder、JDK Mission Control、およびJMC agentは、JVMの性能を監視および診断するための強力なツールのセットを提供します。これらのツールは、最小限の性能コストで、邪魔にならないように動作します。エージェントプラグインは、これらのツールをさらに発展させ、実稼働環境において長くてコスト高になりかねないJVMの再起動を必要とせずに、実行時に独自のフライトレコーダイベントを導入するための容易な方法を提供します。

JMCエージェントの代替品としてBytemanなどがありますが、これらはより一般的な用途に適していることが多いようです。JMCエージェントとエージェントプラグインは、JDK Flight Recorderイベントの作成と導入を目的として設計されており、これらの用途により適しています。JDK Mission Control 8.1のリリースでは、エージェントプラグインがすぐに利用できるようになり、性能監視のためにこれらの手段を活用することがこれまで以上に簡単にできるようになりました。

JMCエージェントとエージェントプラグインは、どちらもまだ発展途上にあります。これらの開発の最新情報を入手し、アイデアやフィードバックを提供するには、jmc-devメーリングリストに参加し、上流のGitHubリポジトリで開発を追跡してみてはいかがでしょうか。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。