レベル: 初級 Toby Corbin (corbint@uk.ibm.com), Software Engineer,
IBM
2007年 10月 16日 alphaWorks から入手できる IBM® Lock Analyzer for Java™ は、実行中の Java アプリケーションで、ロックが起こっていないかをリアルタイムで監視するツールです。このツールを使うと、ロック競合の問題があり、アプリケーションのパフォーマンスを劣化させている可能性があるスレッドが明らかになります。開発者はこの情報を利用して、ロック競合が軽減するようアプリケーションを修正し、パフォーマンスを向上させることができます。今回の記事では IBM Lock Analyzer for Java を取り上げ、その基礎となるアーキテクチャーを説明するとともに、このツールの将来的な方向についての見解を紹介します。
 |
この連載について
連載「Java の診断を IBM スタイルで」では、Java アプリケーションの問題解決を支援し、アプリケーションのパフォーマンスを改善する新たな IBM のツールを取り上げます。毎回掲載される記事から、すぐに実行に移せる新しい知識を得られるはずです。
この連載の寄稿者はいずれも、Java アプリケーションでの問題解決支援ツールを作成するために新しく組織されたチームのメンバーです。さまざまなバックグラウンドを持つ著者たちが、さまざまなスキルとその専門の分野でチームに貢献しています。
記事に関するご意見・ご質問は、それぞれの記事を担当する著者にお寄せください。
|
|
最近の Java アプリケーションの大多数は、言語の機能を利用してスレッドの使用による並行プログラミングをサポートしています。並行プログラミングでは複数のスレッドが同じデータ・オブジェクトを共有することはできても、スレッドを制御するアプリケーションの構成部分がうまく設計されていないと、ロック競合が発生し、それによってパフォーマンスが劣化する可能性があります。
マルチスレッド・アプリケーションでは、例えば複数のスレッドが同じリソースにアクセスして読み取りや書き込みを行う場合、スレッドの同期が問題になります。あるスレッドがファイルに書き込んでいる際に別のスレッドがそのファイルからの読み取りを行おうとすると、データが壊れる可能性があるからです。Java 言語ではこの問題を解決するため、スレッドがオブジェクトの排他的ロックを獲得し、他のすべてのスレッドがそのオブジェクトにアクセスできないようにすることができます。ロックを保持するスレッドは、オブジェクトを処理し終えるとロックを解除して他のスレッドがオブジェクトにアクセスできるようにします。しかし、アプリケーションが慎重に設計されていなければ、このメカニズムによって競合は起こりやすくなります。
ロック競合は、ロックが使用されているときに別のスレッドがロックを獲得しようとすると発生します。つまり、頻繁に取得されるロックや長時間保持されるロック、あるいはその両方の条件を持つロックでは競合が発生しやすいということです。競合しやすいロック (多数のスレッドがアクセスしようとするロック) はシステム内のボトルネックとなり得ます。ロックを獲得するまで実行中スレッドのそれぞれが処理を一時停止し、それによってアプリケーションのパフォーマンスが制限されてしまうためです。
JLA によるロック問題の防止
alphaWorks から入手できる IBM JLA (Lock Analyzer for Java) は、IBM 提供の Java SDK または JRE バージョン 5.0 以降を実行するプラットフォームで稼動し、実行中のアプリケーションでのロックを分析してスレッドのアクティビティーを明らかにすることによって、ロックの競合が発生する頻度を予測できるようにする新しいツールです。このツールに表示される結果から、ロック競合の問題やパフォーマンスに関する問題を解決する手掛かりをつかむことができます。
JLA は既存の Java アプリケーションと連動してアプリケーションのロックに関するあらゆる情報を収集します。それぞれのロックと併せて記録される詳細情報には、ロックが保持された期間、競合の有無、そしてロック保持期間中にそのロックを取得しようとするスレッドをブロックした時間の割合などが含まれます。このツールはランタイム・ライブラリー (監視対象のアプリケーションの起動時にロードする必要があります) と、結果の表示と分析を行うフロントエンドのグラフィカル・ユーザー・インターフェースで構成されます。
JLA の設計詳細
JLA は以下の 2 つのパッケージで構成されます。
-
JLAagent。このパッケージはプラットフォームに依存し、プラットフォーム固有のライブラリー・ファイル、そして 1 つの JAR にまとめてパッケージ化された一連の Java クラスからなります。このパッケージが Java 仮想マシン (JVM) に接続して実行中アプリケーションのロックに関する統計を集めます。
-
JLAGui。Swing で作成されたこのパッケージはプラットフォームには依存しません。このパッケージがグラフィカル・ユーザー・インターフェースを提供します。
JLAagent のネイティブ・ライブラリーは C で記述されており、JVM TI (Java Virtual Machine Tool Interface) を使用します。このインターフェースが提供するのは、JVM 内で動作するアプリケーションの実行を制御するためのメソッドです。このインターフェースから、IBM がリリースした JVM の関数を利用してスレッドの統計を集めます。アプリケーションの起動時に、JVM にこのネイティブ・ライブラリー JVM をロードさせるようにするには、分析対象とする Java アプリケーションの起動コマンドを変更します。ロードされたネイティブ・ライブラリーは、一緒にパッケージ化された Java クラスを使ってプラットフォーム MBean サーバーを作成します。このサーバーが、JLAGui を JLAagent に接続させるためのメカニズムとなります。
MBean サーバーが起動されると、JLAGui はサーバーからネイティブ・ライブラリーにナビゲートし、そこからさらに実行中 JVM にナビゲートするという方法で、実行中アプリケーションのロック統計を要求できるようになります。JLAagent と JLAGui は MBean によって接続されるので、この 2 つを同じマシンで実行する必要はありません。JLAGui はネットワーク上のどこからでも MBean サーバーに接続することができます。
JLA の実行
JLA を実行するには、まず JLAagent を使ってアプリケーションを構成する必要があります。これにより、JLAGui を起動すると同時にロック・パフォーマンスの監視が開始されるようになります。まだこの構成が済んでいない場合は、JLA をダウンロードして以下の説明を読む準備をしてください。
JLA を使用するようにアプリケーションを構成する
JLAagent には複数のバージョンがあり、各バージョンが IBM 提供の JRE でサポートされるそれぞれのプラットフォームに対応します。必ず、監視対象のアプリケーションを実行するプラットフォームに対応したバージョンの JLAagent を使用してください。誤ったバージョンの JLAagent を使用すると、監視対象のアプリケーションは JLAagent のロードに失敗し、異常終了してしまいます。各 JLAagent パッケージの名前は、そのパッケージを呼び出す JRE のアーキテクチャーに基づいています。
JLAagent パッケージを監視対象のアプリケーションを実行するマシンのディレクトリーに解凍し、このディレクトリーを Java クラスパスとシステム・パスに追加してください。パッケージに組み込まれている JLAtiagent.properties という名前のプロパティー・ファイルには、MBean サーバーを起動するための接続情報が含まれています。そのデフォルト値を変更する必要がある場合は、このプロパティー・ファイルを編集してください。
分析するアプリケーションに JLAagent をロードするには、アプリケーションの起動コマンドを変更します。起動コマンドに、-agentlib:JLAtiagent パラメーターを追加してアプリケーションが JLAagent のネイティブ・ライブラリーをロードするようにし、JLAtiagent.jar をクラスパスに追加で指定し、さらに JLAtiagent.properties の場所を指定します。
例えば、起動コマンド java dummy.class は以下のように変更することになります。
java -Dcom.sun.management.config.file=JLAtiagent.properties
-agentlib:JLAtiagent -cp .;JLAagent.jar dummy.class |
この時点で例外が発生した場合は、正しいバージョンの JLAagent を使用していることを確認してください。すべてが正しければ、コマンドラインに JLA Client registering MBeanServer (JLA クライアントが MBeanServer を登録しています) というメッセージが表示されます。
JLAGui が実行中でないとしても、このメッセージが表示された時点で JLAagent は VM にロックの詳細の記録を開始するよう指示します。したがって、JLAGui を起動すると、アプリケーションの存続期間中についてのロック統計を確認することができます。
JLAGui
JLAGui を起動するには、以下のコマンドを実行します。
JLAGui はデフォルトで、ローカル・ホストのポート 1972 で動作中の MBean サーバーを検索するため、ホストやポートの値を指定する必要はありません。ただし別の値を使用したい場合には、表 1 に示すように、コマンドラインでホストまたはポートの値を変更することができます。
表 1. JLAGui の接続オプション
| 設定 | デフォルト | 説明 |
|---|
-Dcom.ibm.jla.port
| 1972 | JLAtiagent が使用する com.sun.management.jmxremote.port の値と一致していなければなりません。 |
-Dcom.ibm.jla.hostname
| localhost | リモート・マシンで動作するアプリケーションを監視する場合は、IP アドレスまたは有効なホスト名に設定します。 |
GUI は起動時に、起動オプションに指定されたマシンおよびポート番号で実行中の MBean サーバーを介してエージェントに接続しようと試みます。接続に成功すると、図 1 の画面が表示されます。
図 1. JLA の初期画面
接続に失敗すると、GUI が試行した接続の詳細を示すエラー・メッセージが表示されます。このエラー・メッセージを調べて、接続が正しいことを確認してください。必要な場合は、いったん GUI を閉じ、コマンドライン・オプションを修正してから再起動します。あるいは、分析対象のアプリケーションがネイティブ・エージェントに接続して実行中であることを確認するという方法もあります。
監視中のアプリケーションの現行スレッド統計のスナップショット・レポートを作成するには、Create Report ボタンをクリックします。すると 3 つのパネル (図 2 を参照) にそれぞれ、Java モニター (アプリケーションにより作成されます)、システム・モニター (VM により作成されます)、サマリー・レポート・ページが表示されます。アプリケーションによって作成または使用されたモニターはすべて、表とグラフにエントリーとして示されます。複数のスレッドがモニターにアクセスしているとしても、モニターごとのエントリーは 1 つだけです。このデータは、スレッド単位ではなくモニター単位で記録されます。
図 2. JLA レポートの例
それぞれの列の高さが基準とするのは低速ロック・カウントの値で、グラフ内のすべての列に相対した高さとなっています。低速カウントは、要求されたモニターがすでに別のスレッドによって所有されているために要求側スレッドがブロックされると発生します。棒グラフの色は %MISS 列 (表 2 を参照) の値に基づき、赤 (100%) から黄色 (50%)、そして緑 (0%) まで変化します。赤い棒グラフは、スレッドがモニターを要求すると毎回ブロックされたことを示し、緑の棒グラフはスレッドが 1 度もブロックされなかったことを示します。このグラフをひと目見るだけで、パフォーマンスに問題があるモニターがわかるはずです。
表 2 に、レポート表のそれぞれの列の値について説明します。
表 2. レポート表の凡例
| 列名 | 説明 |
|---|
| %MISS | 合計取得 (獲得) 回数のうち、要求側スレッドがこのモニターで待機してブロック状態となったパーセンテージ |
|---|
| GETS | 獲得成功の合計回数 |
|---|
| NONREC | 非再帰的獲得の合計回数。この回数には SLOW の取得回数も含まれます。 |
|---|
| SLOW | 要求側スレッドが、モニターが解放されるのを待機してブロック状態になった、非再帰的獲得の合計回数。この回数は、NONREC に含まれます。 |
|---|
| REC | 再帰的獲得の合計回数。再帰的獲得とは、要求側スレッドがすでに該当するモニターを所有しているときに獲得した場合を指します。 |
|---|
| TIER2 | 3 層スピン・ロックをサポートするプラットフォームでの第 2 層 (内部スピン・ループ) の合計繰り返し回数 |
|---|
| TIER3 | 3 層スピン・ロックをサポートするプラットフォームでの第 3 層 (外部スレッド譲歩ループ) の合計繰り返し回数 |
|---|
| %UTIL | モニター保持時間を実行時間 (Interval time) で割ったパーセンテージ。保持時間アカウンティングをオンに設定する必要があります。 |
|---|
| AVER_HTM | モニターの平均保持時間。モニターが再帰的に獲得された場合は、モニターはすでに保持されていることになるので、この時間には再帰的獲得は含まれません。 |
|---|
| MONITOR NAME | モニター名。名前が不明の場合は NULL (空白) になります。 |
|---|
JLA の動作
ロック競合が発生している領域を見つけるために、JLA をどのように使用すれば役に立つかを理解するには、単純なサンプル・サンプル・アプリケーションが有効です。その一例として、同じオブジェクトへのアクセスを試みる 2 つのスレッドで構成されるアプリケーションがあるとします。このオブジェクトは JLAsink というクラスで、2 つの同期化されたメソッドを持っています。一方はデータを設定するためのメソッド、そしてもう一方はデータを取得するためのメソッドです。両方のスレッドは同時に開始され、どちらもループで実行して JLAsink に同時にアクセスしようとします。2 つのスレッドのうち、1 つはセッター・メソッドを呼び出し、もう 1 つは取得メソッドを呼び出すという前提です。
図 3 に示すのは、最適化する前のパフォーマンスが思わしくないサンプル・アプリケーションに対して JLA を実行した場合の結果です。
図 3. JLA の初期スナップショット
上記のスクリーンショットからわかるのは、JLAsink モニターの取得回数は 9,161 で、要求側スレッドはロックを取得するために 25 パーセントの時間ブロックされた状態だったということです。このロックを取得しようとするスレッドを処理するという点では明らかに、このアプリケーションのパフォーマンスは優れていません。
図 4 に、これよりはるかに効率的なバージョンのサンプル・アプリケーションに対して JLA を実行した場合を示します。
図 4. 最適化されたアプリケーションに対して実行した場合の JLA のスナップショット
ご覧のように、JLAsink モニターの取得回数は 9,370 で、そのうちブロックされたものは 1 つもありません。この結果が示しているのは、ロック競合の処理に関してはこのモニターが最適な振る舞いをしているということです。最適化されたコードの実行速度が最適化する前と比べて向上していることは、実行時間 (Interval time) を比較すればわかります。最適化後は、最適化前より JLAsink オブジェクトへのアクセス回数が増え、しかもアクセスに要する時間は短くなっています。
今後の予定
このツールの開発は現在、現行の機能が整った状態で停止しています。この JLA をベースに私たちチームは今後、実行中の Java アプリケーションのロック統計以上の分析を行う新しいツールをリリースしようと思っています。このツールには、JLA だけでなく、この連載の以前の記事で紹介した EVTK および Dump Analyzer ツールの機能も取り込む予定です。このツールは現在、設計段階にあります。
次回の予告
この連載の次回の記事では、第 1 回で紹介した Dump Analyzer ツールを再び取り上げます。ツールの拡張性をさらに深く掘り下げ、独自の分析モジュールを作成する方法を説明する予定です。
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | 
|  | Toby Corbin はソフトウェア・エンジニアで、現在は IBM Java Technology Centre で RAS ツールの開発を行っています。IBM に入社したのは 2001年です。それから 4 年間は Java ランタイム環境の各国語サポートと国際化対応の開発に従事し、続く 2 年間は Swing および AWT ライブラリーの開発に取り組みました。 |
記事の評価
|