web-dev-qa-db-ja.com

Tomcatでアプリケーションを再デプロイするときのメモリリーク

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マネージャーで「メモリリークの検索」オプションを使用しましたが、「メモリリークが見つかりません」と表示されます。

45
Raghav

アプリケーションを再デプロイすると、Tomcatは新しいクラスローダーを作成します。古いクラスローダーはガベージコレクションする必要があります。そうしないと、permgenメモリリークが発生します。

Tomcatはガベージコレクションが機能するかどうかを確認できませんが、いくつかの一般的な障害点については認識しています。 webappクラスローダーがThreadLocalを、webappクラスローダー自体によってロードされたクラスを持つインスタンスに設定する場合、サーブレットスレッドはそのインスタンスへの参照を保持します。つまり、クラスローダーはガベージコレクションされません。

Tomcatはこのような検出を多数行います。 詳細はこちら を参照してください。スレッドローカルのクリーニングは困難です。アクセス元の各スレッドのThreadLocalremove()を呼び出す必要があります。実際には、これは開発中にWebアプリを複数回再デプロイする場合にのみ重要です。実稼働環境では、おそらく再デプロイしないため、これは無視できます。

スレッドローカルを定義しているインスタンスを実際に見つけるには、プロファイラーを使用する必要があります。たとえば、 JProfiler (免責事項:私の会社はJProfilerを開発しています)のヒープウォーカーは、これらのスレッドローカルを見つけるのに役立ちます。報告された値クラス(com.Sun.xml.bind.v2.runtime.property.SingleElementLeafPropertyまたはcom.Sun.xml.bind.v2.ClassFactory)を選択し、累積された着信参照を表示します。それらの1つはJava.lang.ThreadLocal$ThreadLocalMap$Entry。その着信参照タイプの参照オブジェクトを選択し、割り当てビューに切り替えます。インスタンスが割り当てられた場所が表示されます。その情報を使用して、それについて何かできるかどうかを決定できます。

enter image description here

40
Ingo Kegel

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 を参照してください。

7
leeyuiwah

スレッドを正しくクリーンアップせずに作成すると、最終的にはメモリ不足になります。

まだ迅速な解決策/回避策を探している人は、以下に行くことができます:

  • スタンドアロンTomcatを実行している場合は、javaw.exeまたはそれを含むプロセスを強制終了します。
  • Eclipseから実行している場合は、Eclipse.exeおよびJava.exeを終了するか、プロセスを囲みます。
  • まだ解決されていません。タスクマネージャーを確認してください。これを引き起こしているプロセスは、最も高いメモリ使用量で表示される可能性があります-分析を行い、それを強制終了します。

スタッフを再デプロイして、メモリの問題なしで続行するのが良いはずです。

4
Snehal Masne

おそらくこれを見たことがあると思いますが、ehcache docが WEB-INF/libではなくTomcatにlibを置く を推奨している場合に限ります。

2
user2177336

ServletRequestListenerthread 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>
1