Java Platform, Enterprise Edition アプリケーションでのメモリー・リーク
メモリー・リークのタイプはさまざまで、スレッドと ThreadLocal のリーク、ClassLoader のリーク、システム・リソースのリーク、接続のリークなどがあります。 通常、メモリー・リーク検出の方法には、Java™ 仮想マシン・ツール・インターフェース (JVMTI) またはパフォーマンス・モニター・インフラストラクチャー (PMI) カウンターを調べて、Java ヒープまたはネイティブ・ヒープの使用量の増加が遅いかどうかを監視することが含まれます。
クラス・ローダーのメモリー・リーク
メモリー・リークの多くは、クラス・ローダーのリークであることが明らかです。 Java クラスは、その名前と、それをロードしたクラス・ローダーによって一意的に識別されます。 同じ名前のクラスを単一の JVM で複数回、それぞれ異なるクラス・ローダーでロードできます。 各 Web アプリケーションは独自のクラス・ローダーを取得します。これは、 WebSphere® Application Server がアプリケーションを分離するために使用するものです。
オブジェクトは、インスタンスの派生元のクラスへの参照を保持します。 クラスは、ロードを行ったクラス・ローダーへの参照を保持します。 クラス・ローダーは、ロードした各クラスへの参照を保持します。 Web アプリケーションから単一のオブジェクトへの参照を保持すると、Web アプリケーションによってロードされた各クラスが留まります。 これらの参照は、多くの場合、Web アプリケーションが再ロードされた後も保持されます。 再ロードごとに、留まるクラスが増加し、メモリー不足エラーが発生します。
クラス・ローダーのメモリー・リークは、通常、アプリケーション・コードまたは JRE がトリガーしたコードによって発生します。
JRE がトリガーするリーク
メモリー・リークは、Java ランタイム環境 (JRE) コードがコンテキスト・クラス・ローダーを使用してアプリケーション・シングルトンをロードするときに発生します。 これらの singleton は、コンテキスト・クラス・ローダーを使用した JRE によってロードされるスレッドや他のオブジェクトの可能性があります。
- コンテキスト・クラス・ローダーは Web アプリケーション・クラス・ローダーになります。
- 参照が Web アプリケーション・クラス・ローダーに対して作成されます。 この参照は、ガーベッジ・コレクションされません。
- クラス・ローダー、およびクラス・ローダーによりロードされるすべてのクラスをメモリーに留めます。
アプリケーションがトリガーしたリーク
- カスタム ThreadLocal クラス
- ThreadLocal 値としての Web アプリケーション・クラスのインスタンス
- ThreadLocal 値から間接的に保持される Web アプリケーション・クラスのインスタンス
- ThreadLocal 疑似リーク
- Web アプリケーションによって作成された ContextClassLoader およびスレッド
- 共通クラス・ローダーによってロードされたクラスが作成した ContextClassLoader およびスレッド
- 静的クラス変数
- JDBC ドライバーの登録: RMI ターゲット
- 動的キャッシュに保管された Web アプリケーションのクラス・インスタンス
- Web アプリケーションに登録されたカスタム PMI コンポーネント
WebSphere Application Server には、アプリケーションの停止または再デプロイ時にメモリー・リークに対する保護手段がいくつかあります。 WebSphere Application Server は、アプリケーションおよびモジュールのアクティビティーをモニターし、アプリケーションまたは個々のモジュールが停止したときに診断アクションを実行します。
- 検出: メモリー・リークが検出されると警告を出します。 Web アプリケーションが停止、アンデプロイ、または再ロードされたときに、標準的な API 呼び出しと、リフレクションのいくつかのテクニックを組み合わせて利用します。 WebSphere Application Server は、メモリー・リークの既知の原因を検査し、以下のようにアプリケーション・リークが検出されると警告を出します。
[11/17/11 12:01:05:911 EST] 00000005 LeakDetection E CWMML0015E: The web application [WasSwat#WasSwatWeb.war] created a ThreadLocal with key of type [test.memleak.MyThreadLocal] (value [test.memleak.MyThreadLocal@216c691]) and a value of type [test.memleak.MyCounter] (value [test.memleak.MyCounter@21942ff]) but failed to remove it when the web application was stopped.
- 防止はデフォルトでオンになっており、JRE がトリガーするリークにのみ適用されます。 アプリケーション・サーバー・クラス・ローダーがコンテキスト・クラス・ローダーの場合、サーバーの起動時に singleton を初期化することによって、JRE がトリガーするリークが防止されます。
- アクション: メモリー・リークを修正するため、予防的なアクションを取ります。 これらのアクションには、合理的なデフォルトが指定され、状況に応じて構成されます。
protected void com.ibm.ws.classloader.clearReferences(){ if(ENABLE_CLEAR_REFERENCES_JDBC) clearReferencesJdbc(); if(ENABLE_CLEAR_REFERENCES_THREADS) clearReferencesThreads(); if(ENABLE_CLEAR_REFERENCES_THREADLOCALS) clearReferencesThreadLocals(); if(ENABLE_CLEAR_REFERENCES_RMI_TARGETS) clearReferencesRmiTargets(); if(ENABLE_CLEAR_REFERENCES_STATICS) clearReferencesStaticFinal(); }
リークの原因 | 修正方法 | 使用可能にするため、および制御するための WebSphere Application Server Java Virtual Machine プロパティー |
---|---|---|
Threadlocal | 構成可能な期間でスレッド・プールのスレッドを更新します。 プールからスレッドを取り出すと、スレッドと Threadlocal がガーベッジ・コレクションされます。 |
|
HttpClient のキープアライブ・スレッド | スレッドを親クラス・ローダーに切り替えます。 |
|
Timer スレッド | リフレクションを使用して、スケジュール設定されている可能性がある新しいタスクを停止します。 |
|
JVM によって制御されないスレッド | スレッドが実行プログラムを使用して開始されている場合、実行プログラムをシャットダウンするか、スレッドを中断します。 |
|
JDBC ドライバー | Web アプリケーションによって登録され、Web アプリケーションが忘れている JDBC ドライバーを登録抹消します。 |
|
ResourceBundle | このクラス・ローダー、またはこのローダーが親クラス・ローダーである任意のクラス・ローダーによってロードされたバンドルの ResourceBundle キャッシュをクリアします。 |
|
RMI ターゲット | リフレクションを使用して、sun.rmi.transport.ObjectTable.implTable および sun.rmi.transport.ObjectTable.objTable の値をクリアします。 |
|
静的クラス変数 | WebSphere Application Server は、アプリケーション・クラス・ローダーまたはモジュール・クラス・ローダーによってロードされるクラスのすべての静的クラス変数の値を無効にします。 |
|