Java Platform, Enterprise Edition アプリケーションでのメモリー・リーク

メモリー・リークのタイプはさまざまで、スレッドと ThreadLocal のリーク、ClassLoader のリーク、システム・リソースのリーク、接続のリークなどがあります。 通常、メモリー・リーク検出の方法には、Java™ 仮想マシン・ツール・インターフェース (JVMTI) またはパフォーマンス・モニター・インフラストラクチャー (PMI) カウンターを調べて、Java ヒープまたはネイティブ・ヒープの使用量の増加が遅いかどうかを監視することが含まれます。

注: WebSphere Application Server バージョン 8.5 は、実行時にアプリケーション・コード内の疑わしいパターンを監視することで、トップダウンのパターン・ベースのメモリー・リーク検出、防止、およびアクションを提供します。 WebSphere Application Server は、アプリケーションの停止や再デプロイ時のメモリー・リークに対する保護の手段をいくつか備えています。 リークの検出、防止、およびアクションが使用可能な場合、WebSphere Application Server は、アプリケーションとモジュールのアクティビティーをモニターし、アプリケーションまたは個別のモジュールが停止したときに、診断アクションを実行してリークを検出および修正します。 このフィーチャーによって、アプリケーションの頻繁な再デプロイメントにおいても、サーバーの停止および起動を必要とせずにアプリケーションのアップタイムが増加します。

クラス・ローダーのメモリー・リーク

メモリー・リークの多くは、クラス・ローダーのリークであることが明らかです。 Java クラスは、その名前と、それをロードしたクラス・ローダーによって一意的に識別されます。 同じ名前のクラスを単一の JVM で複数回、それぞれ異なるクラス・ローダーでロードできます。 各 Web アプリケーションは独自のクラス・ローダーを取得します。これは、 WebSphere® Application Server がアプリケーションを分離するために使用するものです。

オブジェクトは、インスタンスの派生元のクラスへの参照を保持します。 クラスは、ロードを行ったクラス・ローダーへの参照を保持します。 クラス・ローダーは、ロードした各クラスへの参照を保持します。 Web アプリケーションから単一のオブジェクトへの参照を保持すると、Web アプリケーションによってロードされた各クラスが留まります。 これらの参照は、多くの場合、Web アプリケーションが再ロードされた後も保持されます。 再ロードごとに、留まるクラスが増加し、メモリー不足エラーが発生します。

クラス・ローダーのメモリー・リークは、通常、アプリケーション・コードまたは JRE がトリガーしたコードによって発生します。

JRE がトリガーするリーク

メモリー・リークは、Java ランタイム環境 (JRE) コードがコンテキスト・クラス・ローダーを使用してアプリケーション・シングルトンをロードするときに発生します。 これらの singleton は、コンテキスト・クラス・ローダーを使用した JRE によってロードされるスレッドや他のオブジェクトの可能性があります。

Web アプリケーション・コードが singleton の初期化または静的イニシャライザーをトリガーする場合、次の条件が適用されます。
  • コンテキスト・クラス・ローダーは Web アプリケーション・クラス・ローダーになります。
  • 参照が Web アプリケーション・クラス・ローダーに対して作成されます。 この参照は、ガーベッジ・コレクションされません。
  • クラス・ローダー、およびクラス・ローダーによりロードされるすべてのクラスをメモリーに留めます。

アプリケーションがトリガーしたリーク

アプリケーションがトリガーしたリークのカテゴリーは、次のとおりです。
  • カスタム ThreadLocal クラス
  • ThreadLocal 値としての Web アプリケーション・クラスのインスタンス
  • ThreadLocal 値から間接的に保持される Web アプリケーション・クラスのインスタンス
  • ThreadLocal 疑似リーク
  • Web アプリケーションによって作成された ContextClassLoader およびスレッド
  • 共通クラス・ローダーによってロードされたクラスが作成した ContextClassLoader およびスレッド
  • 静的クラス変数
  • JDBC ドライバーの登録: RMI ターゲット
  • 動的キャッシュに保管された Web アプリケーションのクラス・インスタンス
  • Web アプリケーションに登録されたカスタム PMI コンポーネント
アプリケーション・トリガー・リンクについて詳しくは、「 http://wasdynacache.blogspot.com/2012/01/websphere-classloader-memory-leak.html 」および「 https://www.ibm.com/support/docview.wss?uid=swg1PM39870」を参照してください。

WebSphere Application Server には、アプリケーションの停止または再デプロイ時にメモリー・リークに対する保護手段がいくつかあります。 WebSphere Application Server は、アプリケーションおよびモジュールのアクティビティーをモニターし、アプリケーションまたは個々のモジュールが停止したときに診断アクションを実行します。

WebSphere Application Serverのこのメモリー・リーク機能には、検出、防止、およびアクションの 3 つの部分があります。
  1. 検出: メモリー・リークが検出されると警告を出します。 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. 
  2. 防止はデフォルトでオンになっており、JRE がトリガーするリークにのみ適用されます。 アプリケーション・サーバー・クラス・ローダーがコンテキスト・クラス・ローダーの場合、サーバーの起動時に singleton を初期化することによって、JRE がトリガーするリークが防止されます。
  3. アクション: メモリー・リークを修正するため、予防的なアクションを取ります。 これらのアクションには、合理的なデフォルトが指定され、状況に応じて構成されます。
    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();
    }
表 1. リーク修正の要約のマトリックス
リークの原因 修正方法 使用可能にするため、および制御するための WebSphere Application Server Java Virtual Machine プロパティー
Threadlocal 構成可能な期間でスレッド・プールのスレッドを更新します。 プールからスレッドを取り出すと、スレッドと Threadlocal がガーベッジ・コレクションされます。
  • com.ibm.ws.runtime.component.MemoryLeakConfig.clearReferencesThreadLocal
  • com.ibm.ws.runtime.component.MemoryLeakConfig.renewThreadPoolNames
  • com.ibm.ws.runtime.component.MemoryLeakConfig.threadPoolRenewalDelayFactor
  • com.ibm.ws.util.ThreadPool.DEFAULT_THREAD_RENEWAL_DELAY
HttpClient のキープアライブ・スレッド スレッドを親クラス・ローダーに切り替えます。
  • com.ibm.ws.runtime.component.MemoryLeakConfig.clearReferencesHttpClientKeepAliveThread
Timer スレッド リフレクションを使用して、スケジュール設定されている可能性がある新しいタスクを停止します。
  • com.ibm.ws.runtime.component.MemoryLeakConfig.clearReferencesStopTimerThreads
JVM によって制御されないスレッド スレッドが実行プログラムを使用して開始されている場合、実行プログラムをシャットダウンするか、スレッドを中断します。
  • com.ibm.ws.runtime.component.MemoryLeakConfig.clearReferencesInterruptThreads
JDBC ドライバー Web アプリケーションによって登録され、Web アプリケーションが忘れている JDBC ドライバーを登録抹消します。
  • com.ibm.ws.runtime.component.MemoryLeakConfig.preventJreMemoryLeak
ResourceBundle このクラス・ローダー、またはこのローダーが親クラス・ローダーである任意のクラス・ローダーによってロードされたバンドルの ResourceBundle キャッシュをクリアします。
  • com.ibm.ws.runtime.component.MemoryLeakConfig.preventJreMemoryLeak
RMI ターゲット リフレクションを使用して、sun.rmi.transport.ObjectTable.implTable および sun.rmi.transport.ObjectTable.objTable の値をクリアします。
  • com.ibm.ws.runtime.component.MemoryLeakConfig.preventJreMemoryLeak
静的クラス変数 WebSphere Application Server は、アプリケーション・クラス・ローダーまたはモジュール・クラス・ローダーによってロードされるクラスのすべての静的クラス変数の値を無効にします。
  • com.ibm.ws.runtime.component.MemoryLeakConfig.clearReferencesStatic com.ibm.ws.runtime.component.MemoryLeakConfig.filterPrefixes