NetBean 6.0に付属するプロファイラ(NetBeans Profiler)を使って、リモートで動作する Tomcat をプロファイリングする方法を紹介します。NetBeansについてはプロファイラ機能だけを利用するため、NetBeansによる開発は前提としません(むしろEclipseによる開発を前提としています)。
プロファイルのターゲットを Tomcat としていますが、Tomcatに限らず任意のJavaアプリケーションのプロファイリングが可能です。Linux + Tomcat の組み合わせはよくある構成ですので、この構成をプロファイリングのターゲットとします。
NetBeans Profilerについては、Java デバッグツール NetBeans Profilerでも紹介しましたが、NetBeans のバージョンアップによってインストール方法も変わり、またちょっとした注意点があるため改めてここにまとめることにします。
2008年2月現在、Java開発でもっとも利用されているIDEはEclipseだと思います。EclipseにもTPTPというプロファイラがあり、リモートプロファイリングもできます。
Eclipseを開発で利用しているならば、プロファイリングもEclipse上で行うことができた方がうれしいのですが、筆者が試した限りにおいてはTPTPによるリモートプロファイリングは負荷が大きすぎて、プロファイリングすることができませんでした。
使い方の問題かもしれませんが、ここではこれ以上TPTPについて述べないこととします。

Cent OS上で Tomcat を動作させ、Windowsマシンからリモートプロファイリングを行います。リモートプロファイリングを行うためには、プロファイリング対象(今回はTomcat)が動作するマシンに、プロファイラのRemote Packをインストールする必要があります。このRemote Packはプラットフォーム毎にインストール媒体が異なります。
Javaのバージョンはサーバとクライアントで合わせる必要はありません。サーバ側がJDK1.6, クライアント側がJDK1.5という組み合わせでもOKです。サーバ/クライアント共に 1.5.0_04以上であれば動作するはずです。
サーバのOSはWindows系でももちろんかまわないですが、今回はLinuxということを前提に話を進めていきます。
http://profiler.netbeans.org/へアクセスし、サーバのOSに合わせてRemote Packをダウンロードします。今回はサーバがCent OS(Linux)ですので、Linux x86(32bit)を選択します。64bit環境の場合は64bitの方を選択して下さい。

インストールといってもダウンロードした zip を展開するだけです。Cent OSユーザのホームディレクトリ下に profile ディレクトリを作成し、そこへRemote Packをインストールすることにします。
$ cd ~ $ mkdir profile $ cd profile $ unzip ~/profiler-server-6.0-linux.zip
プロファイルを行う前に、calibrate.shを実行しておく必要があります。
Tomcatを起動するjavaコマンドにパスが通っていることを確認し、calibrate.shを実行します。
$ cd ~/profile/bin $ java -version java version "1.5.0_14" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_14-b03) Java HotSpot(TM) Server VM (build 1.5.0_14-b03, mixed mode) ↑このバージョンがTomcatを起動するJavaのバージョンと同じであることを確認 $ ./calibrate.sh Profiler Agent: JNI On Load Initializing... Profiler Agent: JNI OnLoad Initialized succesfully Starting calibration... Calibration performed successfully For your reference, obtained results are as follows: Approximate time in one methodEntry()/methodExit() call pair: When getting absolute timestamp only: 1.6825 microseconds When getting thread CPU timestamp only: 46.81 microseconds When getting both timestamps: 49.11 microseconds Approximate time in one methodEntry()/methodExit() call pair in sampled instrumentation mode: 0.075 microseconds
calibrate.shを実行すると、ホームディレクトリに .nbprofiler というディレクトリが作られ、calibrate.sh の結果が格納されます。"machinedata.jdk15"というファイルが生成されているはずです(JDK1.6の場合はmachinedata.jdk16)。
$TOMCAT_HOME/bin の catalina.sh を同じディレクトリにコピー(catalinaForProfile.shとします)し、ファイルの先頭付近に以下の記述を加えます。2行で書かれていますが、改行せず1行で記述します。
JAVA_OPTS="-agentpath:/home/oki/profiler/lib/deployed/jdk15/linux/ libprofilerinterface.so=/home/oki/profiler/lib,5140"
もし JDK 1.6で動かす場合は、上記のjdk15をjdk16に変更して下さい。
catalinaForProfile.sh により Tomcat を起動します。
$ ./catalinaForProfile.sh run Using CATALINA_BASE: /home/oki/experiment/apache-tomcat-6.0.14 Using CATALINA_HOME: /home/oki/experiment/apache-tomcat-6.0.14 Using CATALINA_TMPDIR: /home/oki/experiment/apache-tomcat-6.0.14/temp Using JRE_HOME: /usr/local/jdk1.5.0_14 Profiler Agent: Initializing... Profiler Agent: Options: >/home/oki/profiler/lib,5140< Profiler Agent: Initialized succesfully Profiler Agent: Waiting for connection on port 5140 (Protocol version: 8)
すると上記のようなログが出力され、一旦起動が停止します(もしこのログが出ない場合は、Tomcat 起動オプションを再確認して下さい)。
これはプロファイラからアタッチされるのを待機している状態です。この後プロファイラからアタッチを行うことでTomcat が起動し、プロファイリングが可能になります。
尚、この状態では Tomcat の起動が完了していないので、shutdown.sh によるシャットダウンはできません。Ctrl + C も無効です。プロファイラからアタッチした後であれば有効ですが、その前に落としたい場合はプロセスIDを確認してプロセスを kill して下さい。
http://ja.netbeans.org/へアクセスし、「NetBeans IDE 6.0 のダウンロード」リンクをクリックしてダウンロードページを表示します。

プロファイラは Java SE に含まれているので、上図の左端の「Java SE」を含む媒体を選択して下さい。プロファイラが使えればいいということであれば、Java SEが良いでしょう(赤枠で囲った箇所)。
インストーラの指示に従ってインストールしてください。
プロファイル対象がEclipseで開発されている場合、NetBeanの「Eclipse Project Importer」でプロジェクトをインポートしておくと便利です。
プロファイラでパフォーマンスを測定する際、測定のエントリポイントを指定する必要があるのですが、NetBeans上にプロジェクトがあると、プロジェクトからクラスやメソッドを簡単に選択できます。プロジェクトがない場合は、エントリポイントを自分で指定する必要があり、その指定方法はやや複雑です。
必要なのはソースファイルだけであって、ビルドエラーが発生していても問題ありません。実際 Eclipseの Dynamic Web Project をインポートするとビルドエラーが発生しますが、特に問題ありません。
手動でプロジェクトを作ってソースだけコピーしてもよいですが、今回は Eclipse Project Impoterを使ってみます。
NetBeansのメニューからツール>プラグインを選択し、表示されるダイアログの「インストール済み」タブを選択して、「カタログを再読み込み」を選択します。プロキシが必要な場合はダイアログが表示されるので、適切に設定してください。
カタログの再読み込みが行わると、「使用可能なプラグイン」にEclipse Project Impoterが表れます。

Eclipse Project Impoterをチェックし、インストールします。
メニューからファイル>Import Project>Eclipse Project...を選択し、Workspace LocationにEclipseワークスペースフォルダ名を入力します(Eclipseプロジェクトのフォルダ名ではないので注意)。
プロジェクト一覧が表示されるので、インポートするプロジェクトにチェックを入れ、インポートを実行します。インポート後、NetBeansでビルドエラーが発生しても問題ありません。
メニューからプロファイル>プロファイラを接続...を選択し、接続先を「<外部アプリケーション>」として、接続モードの「定義」リンクをクリックします。

ターゲットの種類、J2EE Web/アプリケーションサーバーを上記のように選択し、接続方法をリモートとします。Tomcat 6という項目は無いので Tomcat 5.5を選択します。ちなみにターゲットの種類をアプリケーションとしても接続できます。結局TCPで接続するので、ターゲットの種類やAPサーバの種類には依存しないはずです(最後の説明だけが変わる?)
次のリモートシステム構成の指定では、サーバのIPアドレスとホストOSを選択し、接続先の設定を完了させます。
メニューからツール>オプション>一般を選択し、プロキシ設定を「プロキシなし」にします。本来必要ないはずですが、リモートのTomcatへのTCP接続が影響を受けてしまうようです。
メニューからプロファイル>プロファイラを接続...を選択し、左の監視、CPU、メモリーの中からCPUを選択します。右側のペインがパフォーマンス解析に変わるため、「アプリケーションの一部」をチェックし、その右の定義リンクをクリックします(下図)。

rootメソッドとは、パフォーマンス測定のエントリポイントです。通常WebアプリケーションではHTTPリクエストを受け取るサーブレットが起点になるため、サーブレットかそれに代わるクラスを指定するとよいでしょう。
デフォルトでは「Java コアクラスの除外」フィルタが用意されていますが、org パッケージは除外する設定になっていないので、Tomcatやサードパーティのライブラリまで個々にプロファイルされます。これらサードパーティのライブラリも除外しておくとよいでしょう。そのために新しくフィルタセットを作成します。
「フィルタセットを編集」をクリックし、「追加」でフィルタセットを新規追加して適当な名前を付けます。下のグローバルフィルタにいくつかフィルタが定義されているので、除外するフィルタにチェックを入れます。Apache.orgやNetBeansは除外しておいた方が良いでしょう。その他のライブラリを除外したい場合は「グローバルフィルタを編集...」からフィルタ定義を追加します。

適切なフィルタを選んで、「接続」を押下します。これでサーバ側で待機していたTomcatにプロファイラがアタッチされ、Tomcatが起動します。
もしこの時点や後の測定で NullPointerException が発生した場合は、プロファイラのエラー回避を参照して下さい。
一つのリクエストに対するホットスポットを見つけるため、まず「収集結果をリセット」を押下し、ブラウザから測定対象の操作が行われるリクエストをTomcatへ発行し、その後「スナップショットを作成」を押下します(下図)。

ホットスポットタブに、実行時間の大きいメソッドが順に表示されます。呼び出しツリータブには、エントリポイントからの呼び出しツリーが表示されます。基本的にこの2つのタブを解析してパフォーマンスチューニングを行うことになるでしょう。
メモリ分析を行う場合は、「プロファイラを接続」画面でメモリーを選択します。詳細な解析を行うために、「オブジェクトの作成とガベージコレクションの両方を記録」をチェックし、「割り当てのスタックトレースを記録」にもチェックを入れると良いでしょう(下図)。

プロファイル方法については、Java デバッグツール NetBeans Profilerを参照下さい。
プロファイルを行うアプリケーションによっては以下のような例外が発生するかもしれません。
java.lang.NullPointerException
at org.netbeans.lib.profiler.server.ClassBytesLoader.getClassFileBytes(ClassBytesLoader.java:72)
at org.netbeans.lib.profiler.server.ProfilerInterface.classLoadHook(ProfilerInterface.java:860)
at java.lang.Class.getDeclaredConstructors0(Native Method)
Remote Pack 6.0(profiler-server-6.0-xxxxx)を使ってアノテーションを使用するクラスをプロファイリングしようとすると上記のエラーが出るようです。アノテーションを使用するライブラリには、例えばJAXBやSpring Frameworkなどがあります。
ただ、CVSのソースではこの問題は修正されているようです(ClassBytesLoader.java に 回避コードが加わっている)。そのため次版では改修されていると思いますが6.0で動作させる場合は、CVSのソースを一部取り込む必要があります。具体的には ClassBytesLoader.java のみ置き換えて jfluid-server.jar を再ビルドし、サーバ側のprofiler/lib/以下の同名ファイルと置き換えることで動作します。
実際にビルドし直した jfluid-server.jar は以下からダウンロードできます。もし同様の現象が起こったら試してみてください。