Java Platform, Enterprise Edition アプリケーションでのメモリー・リーク
メモリー・リークのタイプはさまざまで、スレッドと ThreadLocal のリーク、ClassLoader のリーク、システム・リソースのリーク、接続のリークなどがあります。メモリー・リークを検出するためのアプローチには、通常、Java™ ヒープまたはネイティブ・ヒープの使用量の緩やかな増加を監視するための Java Virtual Machine Tool Interface (JVMTI) や、パフォーマンス・モニター・インフラストラクチャー (PMI) カウンターの検査が含まれます。
クラス・ローダーのメモリー・リーク
メモリー・リークの多くは、クラス・ローダーのリークであることが明らかです。Java クラスは、その名前と、これをロードしたクラス・ローダーによって一意的に特定されます。 同じ名前のクラスを単一の JVM で複数回、それぞれ異なるクラス・ローダーでロードできます。Web アプリケーションは、それぞれ独自のクラス・ローダーを取得し、WebSphere Application Server は、アプリケーションを分離するためにこの方法を使用しています。
オブジェクトは、インスタンスの派生元のクラスへの参照を保持します。クラスは、ロードを行ったクラス・ローダーへの参照を保持します。 クラス・ローダーは、ロードした各クラスへの参照を保持します。 Web アプリケーションから単一のオブジェクトへの参照を保持すると、Web アプリケーションによってロードされた各クラスが留まります。 これらの参照は、多くの場合、Web アプリケーションが再ロードされた後も保持されます。再ロードごとに、留まるクラスが増加し、メモリー不足エラーが発生します。
クラス・ローダーのメモリー・リークは、通常、アプリケーション・コードまたは JRE がトリガーしたコードによって発生します。
JRE がトリガーするリーク
メモリー・リークは、Java ランタイム環境 (JRE) のコードがコンテキスト・クラス・ローダーを使用して、アプリケーションの singleton をロードしたときに発生します。 これらの singleton は、コンテキスト・クラス・ローダーを使用した JRE によってロードされるスレッドや他のオブジェクトの可能性があります。
- コンテキスト・クラス・ローダーは Web アプリケーション・クラス・ローダーになります。
- 参照が Web アプリケーション・クラス・ローダーに対して作成されます。 この参照は、ガーベッジ・コレクションされません。
- クラス・ローダー、およびクラス・ローダーによりロードされるすべてのクラスをメモリーに留めます。
アプリケーションがトリガーしたリーク
- カスタム ThreadLocal クラス
- ThreadLocal 値としての Web アプリケーション・クラスのインスタンス
- ThreadLocal 値から間接的に保持される Web アプリケーション・クラスのインスタンス
- ThreadLocal 疑似リーク
- Web アプリケーションによって作成された ContextClassLoader およびスレッド
- 共通クラス・ローダーによってロードされたクラスが作成した ContextClassLoader およびスレッド
- 静的クラス変数
- JDBC ドライバーの登録: RMI ターゲット
現在、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 仮想マシンのプロパティー |
---|---|---|
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 は、アプリケーション・クラス・ローダーまたはモジュール・クラス・ローダーによってロードされたクラスのすべての静的クラス変数の値を無効にします。 |
|