JBoss AS7はなぜ速いのか
JBoss Advent Calendar 2012の11日目のエントリです。JJBugでJBoss AS7の起動がなんでそんな速いのか、という話が出たのでちょっと解説します。
JBoss AS7の起動速度が超高速なのにはいくつか理由があります。
マルチコア、マルチスレッドを有効活用するようになった
JBoss AS7より前のバージョンは起動が遅かったのですが、起動プロセスがほぼシングルスレッドで行われていたので、CPUをきちんと使わずに起動していた、という点がまず一番大きな問題でした。AS7では開発当初からマルチスレッドを意識した起動処理の再デザインが行われ、サービスの依存関係を高速に解決し、依存のないものに関しては全て並行に起動するよう実装されています。
不要サービス遅延起動
使われていないサービスは起動が先延ばしされるようになりました。たとえばOSGiサービスはOSGiバンドルがデプロイされない限り起動しません。クラスタ通信コンポーネントもクラスタ対応アプリケーションがデプロイされない限り起動しません。以前のバージョンではこのような判断は実装されていなかったため、不要なサービスも起動時に全て起動してしまい、最適化するには設定ファイルの変更が必要でした。AS7ではユーザはまったく何もしなくてもこの最適化の恩恵を得られることになり、この遅延起動のおかげで起動時間もかなり短縮されています。
各種インデクス
アプリケションサーバやフレームワークの開発もしくはプロファイリングをしたことがある方が居ればご存知かもしれませんが、デプロイ時にxmlやアノテーション、クラスをリフレクションを利用してスキャンするという、フレームワークを作る際に一般的に必要なプロセスが実は非常に高コストです。
JBoss AS7のデプロイメント処理の中身ですが、org.jboss.as.server.deployment.DeploymentUnitというクラスとなってデプロイメントフレームワークから実際のデプロイ処理を行う各種コンポーネントに渡されます。DeploymentUnitにはAttachmentsというものが付属していて、そこにDeploymentReflectionIndexというクラスインデクスとJandexというアノテーションインデクスが付属しています。
クラスインデクスやアノテーションインデクスを利用してクラス情報を取得できるのですが、ここで一度読み込まれたクラス情報はキャッシュされ、実際にjava.lang.refを呼び出す回数は最小化されます。現在のメジャーなJava実装はjava.lang.refの呼び出し、例えばメソッド一覧取得などを行ってもその結果は全くキャッシュされない(JavaのコアAPIがメモリを消費するようなキャッシュを持つのはそれはそれで良くないと思うのでしょうがない面もある)ので、それに比べるとかなりの高速化ができます。JBoss AS7のデプロイメントは全ての場面でインデクス経由でクラス情報にアクセスするため、クラススキャンが遅い、というアプリケーションサーバにおける不動のパフォーマンスボトルネック問題を解消しています。
高速クラスローダ
AS7はJBoss Modulesというコンポーネントが提供するモジュール環境で動作しています。以前の階層型クラスローダやJava EE環境を実現していたようなフレキシブルなクラスローダは、クラスを見つけるためにクラスローダの移譲処理を何度も行う必要があり遅かったのですが、JBoss Modulesではモジュール毎に探索範囲が限定され、その内部でもパッケージ毎にインデクスされているので、最短パスでクラスをロードできるようになっています。
そんなこんなでJBoss AS7は起動だけではなく、デプロイも高速になっています。明日はModuleClassLoaderをちょっとつっこんで書こうかな。