Health Center API を使用して Java アプリケーションの監視を行う、第 2 回

デッドロック検出アプリケーションにメソッドのプロファイル・ビューを追加する

Health Center の 2.1 リリースには強力な API が含まれています。Java 開発者は、この API を使用することにより、作成する Java アプリケーションに Health Center を組み込んで、その監視機能を活用して問題のトラブルシューティングをすることができます。連載の第 2 回となる今回の記事では、第 1 回の記事で作成したデッドロック検出アプリケーションに、メソッドのプロファイル・ビューを追加し、アプリケーションのどの部分が CPU サイクルを最も消費しているかを示す方法について説明します。

Toby Corbin, Software Engineer, IBM  

Toby CorbinToby Corbin は Health Center 監視ツールの中心的開発者です。彼は他にも、コンシューマビリティー・ツールを IBM Java Technology Centre で開発しています。彼は 2001年に IBM に入社して 4 年間、Java ランタイム環境の各国語サポートやグローバル化に携わってきました。また彼は Swing ライブラリーと AWT ライブラリーを 2 年にわたって開発しました。



2013年 8月 08日

IBM Monitoring and Diagnostics Tools for Java - Health Center (Health Center) は、IBM JVM (Java Virtual Machine) 上で実行されるアプリケーションを監視するための、オーバーヘッドの少ない無料診断ツールおよび API です。この API を使用してできることの詳細は、第 1 回を参照してください。今回の記事では、第 1 回で作成したデッドロック検出アプリケーションに、メソッドのプロファイル・ビューを追加し、アプリケーションのどの部分が CPU サイクルを最も消費しているかを示す方法について説明します。(この記事で取り上げるアプリケーションの完全なソース・コードを入手するには、「ダウンロード」を参照してください。)

システム要件

Health Center API バンドルには、少なくとも Eclipse 3.4 または Eclipse 4.x が必要です。

アプリケーションをテストする

第 1 回では、デッドロック状態を作り出すアプリケーションに対してテストを行いました。今回の記事では、そのアプリケーションを CPU に負荷をかけるための機能をいくつか含むように変更したバージョンを使用します。ソース・コードについては「ダウンロード」を参照してください。この変更したアプリケーションのコード・フラグメントをリスト 1 に示します。

リスト 1. GenerateDeadLock の新しいソース・コードの一部
private class runSlowMethods extends Thread {

   public void run() {
      SlowClassAndMethod1 sCAM1 = new SlowClassAndMethod1();
      SlowClassAndMethod2 sCAM2 = new SlowClassAndMethod2();

      sCAM1.start();
      sCAM2.start();
   }

   private class SlowClassAndMethod1 extends Thread {

      public void run() {
         while (true) {
            slowMethod1();
            try {
               Thread.sleep(2000);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }

      private void slowMethod1() {
         String largeString = new String("a string to add");
         for (int i = 0; i < 1000000; i++) {
            largeString.concat(largeString);
         }

      }
   }

   private class SlowClassAndMethod2 extends Thread {

      public void run() {
         while (true) {
            slowMethod2();
            try {
               Thread.sleep(2000);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }

      private void slowMethod2() {
         HashMap map = new HashMap();
         String largeString = new String("another string to add");
         for (int i = 0; i < 1000000; i++) {
            map.put(i, largeString);
            largeString.concat(largeString);
         }

      }

このプログラムを、このプログラムに接続されている Health Center エージェントとともに起動します。Java 5 SR10 以降、Java 6 SR5 以降、または Java 7 を使用する Health Center エージェントで、このアプリケーションを起動するには、以下のコマンドを実行します (図 1)。

java -Xhealthcenter GenerateDeadlock
図 1. アプリケーションを起動する
コマンドラインでアプリケーションを起動する画面のスクリーン・キャプチャー

Java 5 SR9 またはそれより前のバージョン、Java 6 SR4 またはそれより前のバージョンのいずれかを使用する Health Center エージェントでこのアプリケーションを起動する場合には、以下のコマンドを実行します。

java -agentlib:healthcenter -Xtrace:output=healthcenter.out GenerateDeadlock

IBM Java は、IBM developer kits からダウンロードすることができます。

監視アプリケーションを機能強化するためのコーディングをする

第 1 回で作成した DeadlockDemo アプリケーションを変更するために、Application.java ファイルを開きます (図 2)。

図 2. Application.java を開く
オープンする Application.java コードの場所を示す画面のスクリーン・キャプチャー

Application.java のすべてのコードを、この記事のソース・コードで置き換えます (「ダウンロード」を参照)。リスト 2 に Application.java のコード・フラグメントを示します。

リスト 2. Application.java を置き換えるコード・フラグメント
private class ProfileApplication extends Thread {

   String methodTree = null;

   public void run() {

      try {
         Thread.sleep(10000);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }

      while (keepRunning) {
         analyzeMethods();
         try {
            Thread.sleep(2000);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }

   private void analyzeMethods() {
      ProfilingData profilingData;
      profilingData = hcMon.getProfilingData();

      if (profilingData == null) {
         return;
      }

      MethodProfileData[] mPD = profilingData.getProfilingEvents();
      if (mPD != null) {

         final SimpleProfileData[] sPD = new SimpleProfileData[mPD.length];
         int index = 0;
         for (MethodProfileData mP : mPD) {
            methodTree = new String();
            sPD[index] = new SimpleProfileData();
            sPD[index].setCount(mP.getMethodSampleCount());
            sPD[index].setMethodName(mP.getMethodName());
            MethodProfilingNode[] mPN = mP.getCallingMethods();
            for (MethodProfilingNode node : mPN) {
               if (node instanceof MethodProfilingNode) {
                  walkProfileTree(node);
               }
            }
            sPD[index].setMethodTree(methodTree);
            index++;
         }

         Arrays.sort(sPD);

         display.asyncExec(new Runnable() {
            public void run() {

               profileText.setText("method: "
                     + sPD[0].getMethodName()
                     + "\n		sample count:  "
                     + sPD[0].getCount() + "\n"
                     + sPD[0].getMethodName()
                     + sPD[0].getMethodTree());
               shell.redraw();
            }
         });

      }
   }

このコードは最初のコードとは異なりますが、変更箇所の大半は表示を設定するための変更と、アプリケーションの動作を適切にするための変更です。Health Center API の実際のコードでは、メソッドのプロファイル・データをもっと多く要求します。それについては、次のセクションで説明します。


プロファイル・データを取得する

Health Center はサンプル・ベースのプロファイラーを使用しており、このプロファイラーにより、アプリケーションが実行中に呼び出しているメソッドをもれなくサンプリングしたビューが得られます。アプリケーションが 1 つのメソッドに時間を取られすぎていることがパフォーマンスの問題の原因となっている場合があるため、このプロファイラーはパフォーマンスのボトルネックを特定したい場合に便利です。

プロファイル・データにアクセスするには、スレッド・データにアクセスする場合と似た、以下のような呼び出しを使用します。

ProfilingData クラスのメソッドを使用すると、サンプリングされたすべてのメソッド、それらのメソッドのサンプル・カウント、そして呼び出しの階層構造にアクセスすることができます。

getProfilingEvents() メソッドは、サンプリングされたすべてのメソッドに対するエントリーを持つ MethodProfileData の配列を返します。サンプリングされた各メソッドに対し、getMethodSampleCount() を使用してサンプル・カウントを取得することや、getMethodName() を使用してメソッド名を取得すること、さらには getCallingMethods() を使用して、そのメソッドを呼び出したメソッドを取得することができます。

これらのメソッドを使用すると、最も多くサンプリングされたメソッドを最上位とする呼び出しの階層構造を生成することができます。すると、その情報を使用して、監視対象のアプリケーションのパフォーマンスを微調整することができます。


デッドロックの監視とメソッド・プロファイルのモニター

これで、デッドロックの監視とプロファイルのモニターを行う新しいアプリケーションが用意され、バックグラウンドで実行されてデッドロック状態に陥るプログラムが改訂されました。このデッドロックの監視とプロファイルのモニターを行うアプリケーションを実行し、「Application monitoring (アプリケーション・モニター)」ウィンドウに「Deadlock detected (デッドロック検出)」メッセージが表示されるのを確認してください (図 3)。

図 3. デッドロックを監視する
アプリケーション・モニターで、デッドロックが検出されたときのスクリーン・キャプチャー

これで間違いなくデータが得られました。今度は、これらのデータが意味する内容を明らかにします。


プロファイル・データが示す内容

上部パネルでは、アプリケーションの実行に伴ってサンプル・カウントが増加し、このメソッドが最も CPU サイクルを消費していることを示しています。リスト 3 は最もサンプル・カウントの値が大きいメソッドのみを出力するコードを示しています。

リスト 3. 最もサンプル・カウントの値が大きいメソッドを出力するコード
profileText.setText("method: "
   + sPD[0].getMethodName()
   + "\n		sample count:  "
   + sPD[0].getCount() + "\n"
   + sPD[0].getMethodName()
   + sPD[0].getMethodTree());

ただし、sPD 配列のすべての要素をループ処理することにより、もっと多くのメソッドを出力するように変更することもできます。

Health Center でプロファイラーを実行中に、メソッドがスタック上で Java コードをアクティブに実行すると、そのメソッドはカウントされます。つまり、メソッドのサンプル数が多ければ多いほど、それだけ多くの処理をそのメソッドは行っているということです。ただしサンプル・カウントは、メソッドが呼び出された回数を反映しているわけではない可能性があり、あくまでもアプリケーションの中で対象メソッドが行っている処理の量を他のメソッドと比較して示すものです。

この例では、java.util.HashMap.rehash(int) メソッドが最も多くサンプリングされていることがわかります。このメソッドの呼び出しスタック (つまり、このメソッドの呼び出し元) を見ると、このメソッドは元々 GenerateDeadlock$runSlowMethods$SlowClassAndMethod2.run() での呼び出しから始まっていることがわかります。

この階層構造における次の呼び出しを見ると、この run() メソッドが GenerateDeadlock$runSlowMethods$SlowClassAndMethod2.slowMethod2() を呼び出し、slowMethod2()java.util.HashMap.put() を呼び出していることがわかります。それらによって最終的に rehash() が呼び出されています。

デッドロック・アプリケーションの slowMethod2() メソッドを見ると、HashMap にエントリーを追加するループがあることがわかります (リスト 4)。

リスト 4. HashMap にエントリーを追加するループ
private void slowMethod2() {
   HashMap map = new HashMap();
   String largeString = new String("another string to add");
   for (int i = 0; i < 1000000; i++) {
      map.put(i, largeString);
      largeString.concat(largeString);
   }
}

slowMethod2() メソッドの中にあるそのループは、リスト 5 のようにコメントアウトすることができます。

リスト 5. ループをコメントアウトする
   private void slowMethod2() {
      HashMap map = new HashMap();
      String largeString = new String("another string to add");
//      for (int i = 0; i < 1000000; i++) {
//         map.put(i, largeString);
//         largeString.concat(largeString);
//      }
   }

これで再度テスト・アプリケーションを実行してから、デッドロックの監視とプロファイルのモニターを行う新しいツールを実行すると、先ほどの問題が修正されていることがわかります。新しいメソッドがプロファイル・パネルに表示されます (図 4)。

図 4. 問題が修正され、新しいメソッドが表示される
新しいメソッドがプロファイル・パネルに表示された状態のスクリーン・キャプチャー

これで、このプロセス ― CPU サイクルをよく消費しているメソッドを特定し、パフォーマンスのボトルネックが解決されるように支援するプロセス ― を再び開始することができます。


まとめ

この連載の記事では、Health Center API を使い始めるための基本事項を説明しました。この連載で説明した方法を応用することで、Health Center 内の任意のデータを抽出して使用することができます。例えば WebSphere のメソッドが CPU サイクルをよく消費し始めるようになると、Health Center API を使用して WebSphere メソッドの詳細を取得したり、アプリケーションのメモリーが不足しそうなときには Health Center API を使用してシステム・ダンプをトリガーしたりすることができます。


ダウンロード

内容ファイル名サイズ
Source code for articlej-healthcareapi2-source.zip3KB

参考文献

学ぶために

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=939710
ArticleTitle=Health Center API を使用して Java アプリケーションの監視を行う、第 2 回
publish-date=08082013