web-dev-qa-db-ja.com

Java Webアプリケーションでのメモリリーク

メモリリークがあるように見えるJava WebアプリケーションがTomcat 7で実行されています。アプリケーションの平均メモリ使用量は、負荷がかかると(JConsoleを使用して決定されます)時間とともに直線的に増加します。)使用率がプラトーに達すると、パフォーマンスが大幅に低下します。応答時間は100ミリ秒から[300ミリ秒、2500ミリ秒]になり、これが実際に問題を引き起こしています。

アプリケーションのJConsoleメモリプロファイル: application memory profile

VisualVMを使用して、メモリの少なくとも半分が文字配列(つまり、char [])によって使用されており、文字列のほとんど(ほぼ同じ数、300,000インスタンス)が次のいずれかであることがわかります。 、「コピー」、「マイナーGCの終わり」。これらはすべて、ガベージコレクション通知に関連しているようです。私の知る限り、アプリケーションはガベージコレクタをまったく監視していません。 VisualVMはこれらの文字列のGCルートを見つけることができないため、これを追跡するのに苦労しています。

Memory Analyzerヒープダンプ: heap dump, unreachable memory

なぜメモリ使用量がこのように停滞するのかは説明できませんが、いったんパフォーマンスが低下すると、なぜ低下するのかについての理論はあります。メモリが断片化されている場合、アプリケーションは、新しいリクエストを処理するためにメモリの連続したブロックを割り当てるのに長い時間がかかる可能性があります。

これを組み込みのTomcatサーバーステータスアプリケーションと比較すると、メモリは増加し、横ばいになりますが、私のアプリケーションのように「床」が高くなりません。また、到達できないchar []の数が多くありません。

TomcatサーバーステータスアプリケーションのJConsoleメモリプロファイル: enter image description here

Tomcatサーバーステータスapplicationp:のメモリアナライザーヒープダンプ enter image description here

これらの文字列はどこに割り当てられ、なぜガベージコレクションされないのですか?これに影響する可能性のあるTomcatまたはJava=設定はありますか?これに影響する可能性のある特定のパッケージはありますか?

20
Kevin Kibler

Tomcat\bin\setenv.batから次のJMX設定を削除しました:

set "Java_OPTS=%Java_OPTS% 
    -Dcom.Sun.management.jmxremote=true 
    -Dcom.Sun.management.jmxremote.port=9090
    -Dcom.Sun.management.jmxremote.ssl=false
    -Dcom.Sun.management.jmxremote.authenticate=false"

詳細なメモリヒープダンプを取得できなくなりましたが、メモリプロファイルははるかに良く見えます。 enter image description here

24時間後、メモリプロファイルは同じに見えます。 enter image description here

6
Kevin Kibler

ヒープの分析にはmemoryAnalyzerを使用することをお勧めします。これにより、はるかに多くの情報が得られます。
http://www.Eclipse.org/mat/ スタンドアロンアプリケーションとEclipse組み込みアプリケーションがあります。アプリケーションでjmapを実行し、これで結果を分析するだけです。

3
Zamir

すべてのJavaインストールに付属するjvisualvmをお勧めします。プログラムを起動し、Webアプリケーションに接続します。モニター->ヒープダンプ(サイズによっては)時間がかかる場合があります。ヒープダンプのナビゲーションは非常に簡単ですが、自分で理解する必要があります(あまり複雑ではありません)。 、例えば.

Classes(heapdump内)に移動し、Java.lang.Stringを選択して、右クリックインスタンスビューに表示。その後、システムで現在アクティブな左側のテーブルStringインスタンスが表示されます。 1つのStringインスタンスをクリックすると、いくつかのString優先順位が表示されます右のテーブルのright-upperの部分。valueString

右側のテーブルのbottom-right部分に、thisStringが表示されますインスタンスはから参照されます。ここでは、ほとんどの* String *がどこから参照されているかを確認する必要があります。しかし、あなたのケース(176/210、すぐに問題の原因となるStringの例を見つける良い可能性)の場合、問題がどこにあるかを調べた後、明らかになるはずです。嘘。

2
Manuel

まったく別のアプリケーションで同じ問題が発生したので、Tomcat7はおそらく非難されません。メモリアナライザーは、プロセス(約2か月間実行されている)に10Mの到達不能な文字列インスタンスを表示し、それらのほとんどまたはすべてにガベージコレクションに関連する値(たとえば、「割り当てエラー」、「マイナーGCの終わり」)があります。

メモリーアナライザー

Memory Analyzer

フルGCは2秒ごとに実行されますが、これらの文字列は収集されません。私の推測では、GCコードのバグにヒットしました。次のJavaバージョンを使用します。

$ Java -version
Java version "1.7.0_06"
Java(TM) SE Runtime Environment (build 1.7.0_06-b24)
Java HotSpot(TM) 64-Bit Server VM (build 23.2-b09, mixed mode)

および次のVMパラメータ:

-Xms256m -Xmx768m -server -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC 
-XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:NewSize=32m -XX:MaxNewSize=64m
-XX:SurvivorRatio=8 -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails 
-Xloggc:/path/to/file
2
Oliver Sledge

プラトーは、使用可能なメモリがデフォルトのパーセンテージしきい値を下回り、フルGCが発生するために発生します。これは、JVMがメモリを見つけて解放しようとしている間、JVMが常に一時停止しているため、パフォーマンスが低下する理由を説明しています。

通常はオブジェクトキャッシュを確認することをお勧めしますが、あなたの場合、Tomcatインスタンス+ webappに対してヒープサイズが小さすぎると思います。ヒープを1Gに増やすことをお勧めします(-Xms1024m -Xmx1024m)その後、メモリ使用量をもう一度確認してください。

それでも同じ種類の動作が見られる場合は、別のヒープダンプを取り、文字列と文字の後に最大のコンシューマーを確認する必要があります。私の経験では、これは通常キャッシュメカニズムです。メモリをさらに増やすか、可能であればキャッシュストアを減らします。一部のキャッシュはオブジェクトの数のみを定義するため、キャッシュされた各オブジェクトの大きさを理解する必要があります。

メモリー使用量を理解したら、再びそれを下げることができるかもしれませんが、IMHO 512MBが最小になります。

更新:

到達できないオブジェクトはGCによってクリーンアップされるため、心配する必要はありません。また、タイプごとの最大のコンシューマはStringとCharであるのが普通です。ほとんどのオブジェクトにはなんらかのStringが含まれるため、StringsとCharsが頻度で最も一般的であることは理にかなっています。文字列を含むオブジェクトを保持するものを理解することは、メモリコンシューマを見つけるための鍵です。

2

偶然にも、Tomcatのconf/catalina.propertiesファイルで、文字列のキャッシュをアクティブにする次の行に出くわしました。これらのいずれかをオンにしている場合、これはあなたのケースに関連している可能性があります。 他は警告です 機能を使用するようです。

Tomcat.util.buf.StringCache.byte.enabled=true
#Tomcat.util.buf.StringCache.char.enabled=true
#Tomcat.util.buf.StringCache.trainThreshold=500000
#Tomcat.util.buf.StringCache.cacheSize=5000
1
Mathias Lieber

MATを使用してみて、ヒープダンプを解析するときに、到達できないオブジェクトを削除しないようにしてください。

これを行うには、チュートリアル here に従ってください。

その後、簡単なメモリリーク分析を実行できます( これ は良いチュートリアルです)

それはすぐに根本的な原因にあなたを導くはずです。

0
Denny Alquinta

これは不特定に聞こえるので、候補の1つはJSFでした。しかし、私はハッシュマップもリークすることを期待していました。

JSFを使用する場合:web.xmlでは、次のことを試すことができます。

  • javax.faces.STATE_SAVING_METHOD クライアント
  • com.Sun.faces.numberOfViewsInSession
  • com.Sun.faces.numberOfLogicalViews 1

ツールについて:JavaMelodyは、継続的な統計には興味深いかもしれませんが、努力が必要です。

0
Joop Eggen