プロファイラとは、実行中のプログラムの動作を分析するツールである。メソッドの処理時間や呼び出し関係を統計情報として提供するメソッドプロファイラと、メソッドから生成されたオブジェクトの数やサイズ、世代情報を提供するオブジェクトプロファイラの2つが存在する。
メソッドプロファイラは、HWリソースモニタリングツールでAPサーバのCPUがボトルネックになっているときに有効である。また、スレッドダンプ解析で問題が発見できなかった場合に使用する。
一方、オブジェクトプロファイラは、OutOfMemoryエラーが発生しているときや、GCログ分析でヒープ使用量が右肩上がりになっているときに、巨大なオブジェクトや無駄なオブジェクト生成をしているコードを発見するときに使用する。
実際に、コードがどのように動作しているか把握することのできるプロファイラ導入効果は非常に大きい。しかしながら、導入のイニシャルコストやアプリケーションに与える影響などデメリットも大きい。そのため、これまで紹介してきたツールなどでアプリケーションのつくりに問題の原因があると、アタリを付けてから導入するようにしている。
メソッドプロファイラは以下の観点で分析を行う。
筆者は総実行時間の上位10件に注目しソースコードを再度確認している。経験上、上位10件を確認しておけば、ボトルネックとなっている個所をソースコードレベルで改善できる。
また、メソッド1回当たりの実行時間が短い場合でも、そのメソッドが大量に呼ばれている場合、メソッドの総実行時間(呼び出し回数×1回当たりの実行時間)が大きくなりボトルネックになる。そのため分析は総実行時間で行うのがポイントである。
上記リスト(1)の場合は、ヒープ領域を圧迫しGCが発生する原因となる。(2)の場合は、世代別GCのメリットを享受しにくくなってしまう。そのため、オブジェクトの生存期間は短いほどよいとされている。
編集部注:世代別GCについて詳細を知りたい読者は、チューニングのためのJavaVM講座の後編「ガベージコレクタの仕組みを理解する」をご参照ください
(3)の典型例は、静的なコレクションクラスにオブジェクトを追加し続け、削除しない場合である。こういった場合、アプリケーション起動後一定時間が経過すると、そのうちOutOfMemoryエラーが発生し、システムが停止してしまう。
図7はNetBeanProfilerでのオブジェクトプロファイリング実行例である。生成されているオブジェクト名とその数、サイズ、世代が表示されている。
編集部注:NetBeanProfilerについて詳細を知りたい読者は、連載Java開発の問題解決を助けるの第2回「プロファイラでメモリリークとパフォーマンス問題を解決」をご参照ください
プロファイラはその特性上、システムに与えるオーバーヘッドが非常に大きい。筆者の環境では、JVMに標準搭載されているhprofの場合、3秒足らずで起動していたTomcatの起動時間が1分以上に増加した。オーバーヘッドが大きくなると、高負荷時に問題が発生する場合、システムに負荷をかけきれず問題の再現が難しくなる。
また、問題を再現させるために時間がかかってしまい、早急にトラブルを解決できなくなってしまう。さらに、プロファイラはJVM依存の機能を利用することが多く、JVMのベンダや実行されるOSに対する制限が存在する。
有償ではあるが、JVMのプロファイラNeckLess(NTTデータ開発、ニューソン販売)を利用すると、これまで懸案だったプロファイラのオーバーヘッドを抑えるとともに、さまざまな環境(OS/JVM/APサーバ)で活用することが可能となる。筆者はこのNeckLessを用いて、ストレスのないプロファイリングを行っている。プロファイリングにストレスを感じる方は、試してみるとよいだろう。
JMXはJava SE 5.0、J2EE 1.4以上で導入された、Javaアプリケーションを監視、管理するための仕組みのことである。APサーバにJMXクライアントを接続し、アプリケーションの状態を監視、管理できる。
スレッドダンプ解析によって、プーリングオブジェクトの使用状況に問題が発生している可能性が高い結果が得られたときに利用する。JDBCコネクションプールやコネクタスレッドといったプーリングオブジェクトのMBeanに接続し、使用状況を監視する。
また、JMXクライアントはGC発生状況やスレッド使用を取得する機能を備えている。そのため、大まかにシステムの状態を把握したいときにも、有効である。
筆者はプーリングオブジェクトの分析を行うために以下のMBeanを監視している。
利用できるJMXクライアントはMC4Jやjconsoleなどの多くのツールが世の中に存在するが、筆者は利用時のオーバーヘッドなどを考慮し上記の4つのMBeanのみを監視し、ログとして保存したいため、100行程度のJavaで記述したJMXクライアントを作成している。
コネクタスレッドは以下の観点で分析を行う。
コネクタスレッドはユーザーからのリクエストを受け付けるリソースであるため枯渇した場合、ユーザーリクエストは処理待ち状態となり著しくパフォーマンスが低下する。そのため、想定されるユーザー数のリクエストを同時に処理できる程度のコネクタスレッドをあらかじめ生成しておくことが理想的である。ただし、あまりにも多くのコネクタスレッドを生成してしまうと、無駄なリソースを消費してしまう。
JDBCコネクションプールは以下の観点で分析を行う。
システムが無応答になる原因の1つにプールオブジェクトの開放漏れがある。JDBCコネクションプールを獲得した後、JDBCコネクションプールを開放しないと、JDBCコネクションプールをすべて使い尽くし、システムは無応答になる。
これまで紹介したツールを駆使し、システムの状況を「見える化」することで、分析作業が非常にやりやすくなる。ここでトラブル切り分け解析フローを振り返りたい。
賢明な読者の皆さんはすでに気付いているかもしれないが、トラブル切り分けフローは上から下に流れるにつれ、ツールの利用のコストが掛かるようになっている。
障害を切り分けるときは、安易な憶測で適当にツールを利用するより、簡単に分析できるハードウェアリソース情報やGCログなど調べたうえで、考えられる障害の原因の範囲を狭めながら、詳細に調査をしていく方が効率の良い切り分けができる。適切な順序で適切なツールを導入して発生している問題を切り分け、無駄な分析を行わずに済むようにしていただきたい。
地道にボトルネックの直接の原因となっている部分を切り分けてトリルダウンすることが、結果的に迅速なトラブルシューティングにつながっていく。
トラブルシューティングの現場で迅速に問題を解決するためには、適切なタイミングで適切なツールを使うことが重要である。今回紹介した7つ道具が皆さんの迅速なトラブルシューティングの糸口になれば幸いである。
Copyright © ITmedia, Inc. All Rights Reserved.