8. EJBコンテナ

ExpressおよびStandard/EnterpriseにおけるEJBコンテナのチューニング方法に関して説明します。

8.1. EJB種類別のチューニング

EJBには、ステートフルセッションBean、ステートレスセッションBean、エンティティBean、メッセージドリブンBeanの4種類があり、用途に応じて使い分けられます。

8.1.1. ステートレスセッションBeanのチューニング

ステートレスセッションBeanは名前のとおり状態(ステート)を持ちません。そのため事前にインスタンスを生成し、プールしておくことが可能です。インスタンス生成はCPUリソースを消費し、 時間がかかりますので、運用中のインスタンス生成を避けるために事前生成とプーリングが行われます。

また複数クライアントから同時に呼び出しが行われることを想定して、複数のインスタンスをプールしておくことも可能です。プールするインスタンス数を増やすと、プール数に比例してメモリを消費します。

ステートレスセッションBeanのチューニングはインスタンスプールのプール数の調整によって行われます。これはエディションによって設定項目が変わります。

Standard/Enterpriseの場合

リモートインタフェースを使用した場合、プールするインスタンス数はプロセスグループのスレッド数と同じ値になります。 [APサーバ > 性能チューニング > 多重度のチューニング ] を参考にスレッド数を設定してください。

配備されたアプリケーション単位でプールするインスタンス数を調整することも可能です。以下のどちらかの方法により0〜スレッド数の間で設定してください。

ローカルインタフェースを使用した場合、プールするインスタンス数はEJBコンテナの設定項目である「通常プールサイズ」で設定されます。既定値では「通常プールサイズ」は0になっています。インスタンス事前生成機能を有効にするには、「通常プールサイズ」を1以上にしてください。
設定方法は、次の「Expressの場合」を参照してください。

Expressの場合

プールするインスタンス数はEJBコンテナの設定項目である「通常プールサイズ」で設定されます。既定値では「通常プールサイズ」は0になっています。インスタンス事前生成機能を有効にするには、「通常プールサイズ」を1以上にしてください。

8.1.2. ステートフルセッションBeanのチューニング

ステートフルセッションBeanは状態を持ちますので事前生成は行えません。クライアントからステートフルセッションBeanが利用される時、必ずインスタンス生成/破棄が行われます。状態が必要ないアプリケーションの場合、ステートレスセッションBeanで設計し、事前生成・プーリング機能を有効にした方が実行性能は良くなります。

ステートフルセッションBeanではプールの代わりにキャッシュが使われます。メモリ上に作られ、破棄されていないインスタンスが長時間アクセスされない場合、状態がファイルにキャッシュされます。頻繁にキャッシングが行われる場合、アプリケーションの問題(クライアントからbeanのremove()メソッドが呼ばれていない)もしくはインスタンスのタイムアウト設定時間が短すぎることが考えられます。インスタンスのタイムアウト設定時間は次のように設定します。

8.1.3. エンティティBeanのチューニング

エンティティBeanはプールとキャッシュ両方を利用します。プーリングについては [ EJBコンテナ > EJB種類別のチューニング > ステートレスセッションBeanのチューニング ] 、 キャッシングについては [ EJBコンテナ > EJB種類別のチューニング > ステートフルセッションBeanのチューニング ] を参照してください。

8.1.4. メッセージドリブンBeanのチューニング

ステートレスセッションBeanと同様に事前生成・プーリングが行われます。 [ EJBコンテナ > EJB種類別のチューニング > ステートレスセッションBeanのチューニング ] を参照してください。ただしExpressでの通常プールサイズの設定はEJBコンテナではなく、独自のMDBコンテナの設定に拠ります。

8.2. EJBメソッド呼び出しのパフォーマンスの改善

EJBのリモートメソッド呼び出しの際に、パフォーマンス劣化が発生する場合があります。 典型的な例では、EJBのリモートメソッドのメソッド引数やメソッド戻り値を、CollectionインタフェースやMapインタフェースの実装クラス にした場合が挙げられます。このパフォーマンス劣化の度合は、引数や戻り値オブジェクトの要素数に比例します。

引数や戻り値に独自に定義したクラスを使った場合でも、参照するオブジェクトが多いクラスや参照のネストが深くなるクラスでは、同様にパフォーマンスが劣化します。

これは、リモートインタフェースを使用したEJBでは、既定ではオブジェクトが値渡し(call by value)されるからです。値渡しをするためには、 オブジェクトをコピーする必要がありますが、これはJavaのシリアライズ機構を用いたディープコピーになります。 オブジェクトの参照構造の末端までを辿って全てのオブジェクトをシリアライズする必要があるため、参照するオブジェクトが多いクラスや参照のネストが深くなるクラスは 処理量が増え、時間を消費することになります。

この問題に対処するために、WebOTXでは、引数および戻り値オブジェクトを参照渡し(call by reference)する仕組みが用意されています。 参照渡しを行えば、通常のJavaメソッド呼び出しと同じく、メソッドに渡されるのはオブジェクトのポインタになりますので、オブジェクトコピーのオーバーヘッドが発生しません。 参照渡しには、以下の2つの方法があります。どちらも呼び出し元と呼び出し先が同じプロセスで動作していることが必須条件です。

8.2.1. ローカルインタフェースの使用

ローカルインタフェースを使用することにより、EJBメソッド呼び出しを参照渡しにすることが可能です。 ローカルインタフェースはEJB 2.0より定義されています。

同一プロセス内での呼び出しが発生する場合は、設計の段階でローカルインタフェースの採用を検討し、パフォーマンスの向上を図ることを推奨します。

8.2.2. call-by-reference

NEC固有配備記述子 nec-ejb-jar.xml の中で ejb 要素直下にpass-by-reference 要素を追加し、値を true に指定することにより、リモートインタフェースを使用したEJBに対しても 参照渡しをさせることができます。システム開発の後期でパフォーマンス問題が発生し、EJBのインタフェースの設計を変更できない場合に有効です。

ただし、ローカル呼び出しができていないとこれは機能しません。ローカル呼び出しの詳細については、 [ クラスローダ > ライブラリの配置計画 ] を参照してください。

8.3. staticclassgenによるパフォーマンスの改善(EJB 3.0以上)

EJB 3.0以上を動作させるのに、アプリケーション開発者が作成しなければならないインタフェースは、ビジネスインタフェースのみです。 煩雑なリモートインタフェースとホームインタフェースは必須ではなくなりました。

しかしながら、内部的には、クライアントアプリケーションはEJBメソッド実行時、リモートインタフェースとホームインタフェースを必要とします。 そのため、既定ではクライアント側で、ビジネスインタフェース名での初回のlookup時(@EJBアノテーションを利用しているときは、DI解決時)に、 ビジネスインタフェースを元に動的にリモートインタフェースとホームインタフェースを生成します。 一度生成されたリモートインタフェースとホームインタフェースは、クライアントJavaプロセス内のメモリ中で保持されます。 よって、クライアントJavaプロセスからの2回目以降のビジネスインタフェース名でのlookupはEJB 2.1と同等の早さですが、初回lookupはEJB 2.1よりも遅くなります。

WebからEJBを呼び出したり、EJBからEJBを呼び出したりするアプリケーションでは、一般的にこの初回lookupのオーバーヘッドはあまり問題になりません。 Javaプロセスのライフタイムが長いからです。 問題となる場合があるのは、Javaプロセスのライフタイムが一般的に短い、アプリケーションクライアントです。

もし、EJB 3.0以上の初回呼び出しのオーバーヘッドが問題になる場合は、staticclassgenを用いて、静的にリモートインタフェースとホームインタフェースを生成し、 アプリケーションクライアントのクラスパスに追加してください。 [ 操作方法 > その他機能 > staticclassgen ]

staticclassgenで静的生成されたクラスはビジネスインタフェースと同じクラスローダでロードされるように配置してください。 アプリケーションクライアントの場合、ビジネスインタフェースが-client引数で指定されたファイル内にあるなら、-appcpathで静的生成されたクラスを指定すると同じクラスローダでロードされます。 サーバ上で動作するアプリケーション(Webアプリケーション、EJBアプリケーション)のクラスローダについては、 [ クラスローダ ] を参照してください。 クラスローダが違う場合、lookupでjava.lang.NoClassDefFoundError(ビジネスインタフェースが静的生成クラスよりも下位のクラスローダにある場合)あるいはjava.lang.ClassCastException(ビジネスインタフェースが静的生成クラスよりも上位のクラスローダにある場合)が発生します。