Tomcatでアプリケーションを再デプロイすると、次の問題が発生します。
The web application [] created a ThreadLocal with key of type
[Java.lang.ThreadLocal] (value [Java.lang.ThreadLocal@10d16b])
and a value of type [com.Sun.xml.bind.v2.runtime.property.SingleElementLeafProperty]
(value [com.Sun.xml.bind.v2.runtime.property.SingleElementLeafProperty@1a183d2]) but
failed to remove it when the web application was stopped.
This is very likely to create a memory leak.
また、私のアプリケーションでehcacheを使用しています。これにより、次の例外も発生するようです。
SEVERE: The web application [] created a ThreadLocal with key of type [null]
(value [com.Sun.xml.bind.v2.ClassFactory$1@24cdc7]) and a value of type [Java
.util.WeakHashMap...
Ehcacheは弱いハッシュマップを作成するようであり、これによりメモリリークが発生する可能性が非常に高いというメッセージが表示されます。
私はネット上で検索し、これを見つけました http://jira.pentaho.com/browse/PRD-3616 ですが、そのようなサーバーにアクセスすることはできません。
これらの警告が機能に影響するか、無視できるかどうかを教えてください。 Tomcatマネージャーで「メモリリークの検索」オプションを使用しましたが、「メモリリークが見つかりません」と表示されます。
アプリケーションを再デプロイすると、Tomcatは新しいクラスローダーを作成します。古いクラスローダーはガベージコレクションする必要があります。そうしないと、permgenメモリリークが発生します。
Tomcatはガベージコレクションが機能するかどうかを確認できませんが、いくつかの一般的な障害点については認識しています。 webappクラスローダーがThreadLocal
を、webappクラスローダー自体によってロードされたクラスを持つインスタンスに設定する場合、サーブレットスレッドはそのインスタンスへの参照を保持します。つまり、クラスローダーはガベージコレクションされません。
Tomcatはこのような検出を多数行います。 詳細はこちら を参照してください。スレッドローカルのクリーニングは困難です。アクセス元の各スレッドのThreadLocal
でremove()
を呼び出す必要があります。実際には、これは開発中にWebアプリを複数回再デプロイする場合にのみ重要です。実稼働環境では、おそらく再デプロイしないため、これは無視できます。
スレッドローカルを定義しているインスタンスを実際に見つけるには、プロファイラーを使用する必要があります。たとえば、 JProfiler (免責事項:私の会社はJProfilerを開発しています)のヒープウォーカーは、これらのスレッドローカルを見つけるのに役立ちます。報告された値クラス(com.Sun.xml.bind.v2.runtime.property.SingleElementLeafPropertyまたはcom.Sun.xml.bind.v2.ClassFactory)を選択し、累積された着信参照を表示します。それらの1つはJava.lang.ThreadLocal$ThreadLocalMap$Entry
。その着信参照タイプの参照オブジェクトを選択し、割り当てビューに切り替えます。インスタンスが割り当てられた場所が表示されます。その情報を使用して、それについて何かできるかどうかを決定できます。
Mattias Jiderhamnには優れた 6部構成の記事 があり、クラスローダーリークに関する理論と実践を非常に明確に説明しています。さらに良いことに、彼はまた、warファイルに含めることができるjarファイルをリリースしました。私は自分のWebアプリで試してみましたが、jarファイルはとても魅力的でした! jarファイルは、classloader-leak-prevention.jarと呼ばれます。使用するには、これをweb.xmlに追加するだけです。
<listener>
<listener-class>se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor</listener-class>
</listener>
そして、これをpom.xmlに追加します
<dependency>
<groupId>se.jiderhamn</groupId>
<artifactId>classloader-leak-prevention</artifactId>
<version>1.15.2</version>
</dependency>
詳細については、 GitHubでホストされているプロジェクトのホームページ または 彼の記事のパート6 を参照してください。
スレッドを正しくクリーンアップせずに作成すると、最終的にはメモリ不足になります。
まだ迅速な解決策/回避策を探している人は、以下に行くことができます:
- スタンドアロンTomcatを実行している場合は、javaw.exeまたはそれを含むプロセスを強制終了します。
- Eclipseから実行している場合は、Eclipse.exeおよびJava.exeを終了するか、プロセスを囲みます。
- まだ解決されていません。タスクマネージャーを確認してください。これを引き起こしているプロセスは、最も高いメモリ使用量で表示される可能性があります-分析を行い、それを強制終了します。
スタッフを再デプロイして、メモリの問題なしで続行するのが良いはずです。
おそらくこれを見たことがあると思いますが、ehcache docが WEB-INF/libではなくTomcatにlibを置く を推奨している場合に限ります。
ServletRequestListener で thread locals を初期化することをお勧めします。
ServletRequestListener
には、初期化用と破棄用の2つのメソッドがあります。
この方法で、ThreadLocal
をクリーンアップできます。例:
public class ContextInitiator implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
context = new ThreadLocal<ContextThreadLocal>() {
@Override
protected ContextThreadLocal initialValue() {
ContextThreadLocal context = new ContextThreadLocal();
return context;
}
};
context.get().setRequest(sre.getServletRequest());
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
context.remove();
}
}
web.xml
:
<listener>
<listener-class>ContextInitiator</listener-class>
</listener>