ヒープなどのサイズが安定しているにもかかわらず、Sun JVM がさらに多くの RSS メモリを消費し続けるのはなぜですか?

StackOverflow https://stackoverflow.com/questions/1612939

質問

過去 1 年間、私はアプリケーションの Java ヒープ使用量を大幅に改善し、確実に 66% 削減しました。それを追求するために、私は Java ヒープ サイズ、CPU、Java 非ヒープなどのさまざまなメトリクスを監視してきました。SNMP経由。

最近、JVM による実メモリ (RSS、常駐セット) の量を監視しているのですが、少し驚いています。JVM によって消費される実際のメモリは、アプリケーションのヒープ サイズ、非ヒープ、Eden 領域、スレッド数などとは完全に独立しているようです。

Java SNMP によって測定されたヒープ サイズ Java ヒープ使用量グラフ http://lanai.dietpizza.ch/images/jvm-heap-used.png

実メモリ (KB)。(例えば。:1 MB の KB = 1 GB) Java ヒープ使用量グラフ http://lanai.dietpizza.ch/images/jvm-rss.png

(ヒープ グラフの 3 つの落ち込みは、アプリケーションの更新/再起動に対応します。)

JVM が消費している余分なメモリはすべて、OS がファイル キャッシュに使用できるメモリを「盗んでいる」ため、これは私にとって問題です。実際、RSS 値が約 2.5 ~ 3 GB に達すると、アプリケーションの応答時間の低下と CPU 使用率の上昇が見られ始めますが、これは主に IO 待機によるものです。ある時点で、スワップ パーティションへのページングが開始されます。これはすべて非常に望ましくないことです。

それで、私の質問は次のとおりです。

  • なぜこうなった?何が起こっているのか "フードの下"?
  • JVM の実メモリ消費を抑えるにはどうすればよいでしょうか?

悲惨な詳細:

  • RHEL4 64 ビット (Linux - 2.6.9-78.0.5.ELsmp #1 SMP 9 月 24 日水曜日 ...2008 x86_64 ...GNU/Linux)
  • Java 6 (ビルド 1.6.0_07-b06)
  • トムキャット 6
  • アプリケーション (オンデマンド HTTP ビデオ ストリーミング)
    • java.nio FileChannels による高 I/O
    • 数百から数千スレッド程度
    • データベースの使用率が低い
    • 春、冬眠

関連する JVM パラメータ:

-Xms128m  
-Xmx640m  
-XX:+UseConcMarkSweepGC  
-XX:+AlwaysActAsServerClassMachine  
-XX:+CMSIncrementalMode    

-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps  
-XX:+PrintGCApplicationStoppedTime  
-XX:+CMSLoopWarn  
-XX:+HeapDumpOnOutOfMemoryError 

RSS の測定方法:

ps x -o command,rss | grep java | grep latest | cut -b 17-

これはテキスト ファイルに保存され、監視システムの RRD データベースに定期的に読み込まれます。ps はキロバイトを出力することに注意してください。


問題と解決策s:

結局そうだったのに アトラスの答えが最終的に正しいことが判明した、それは kdgregory を使用して正しい診断パスに私を導いてくれたのは pmap. 。(両方の回答に投票してください!) 何が起こったかは次のとおりです。

私が確かに知っていること:

  1. 私のアプリケーションはデータを記録および表示します JRobin 1.4, 、3年以上前にアプリにコーディングしたものです。
  2. 現在作成されているアプリケーションの最もビジーなインスタンス
    1. 起動後 1 時間以内に 1000 個を超える新しい JRobin データベース ファイル (それぞれ約 1.3MB)
    2. 開始後毎日最大 100 件以上
  3. 書き込むものがある場合、アプリはこれらの JRobin データベース オブジェクトを 15 秒ごとに更新します。
  4. デフォルト設定 JRobin では次のようになります。
    1. を使用します java.nio-ベースのファイル アクセス バックエンド。このバックエンド マップ MappedByteBuffers ファイル自体に。
    2. 5 分に 1 回、JRobin デーモン スレッドが呼び出します MappedByteBuffer.force() すべての JRobin の基礎となるデータベース MBB
  5. pmap リストされています:
    1. 6500のマッピング
    2. そのうち 5500 個は 1.3MB の JRobin データベース ファイルで、約 7.1GB になります。

最後のポイントは私のものでした "ユーレカ!" 一瞬。

私の是正措置:

  1. 明らかに優れている最新の JRobinLite 1.5.2 に更新することを検討してください。
  2. JRobin データベースに適切なリソース処理を実装します。現時点では、アプリケーションは一度データベースを作成し、データベースがアクティブに使用されなくなった後はそれをダンプすることはありません。
  3. 動かして実験してみる MappedByteBuffer.force() 定期的なタイマーではなく、データベース更新イベントに使用されます。問題は魔法のように解決するのでしょうか?
  4. すぐに, 、JRobin バックエンドを java.io 実装に変更します (行の変更)。これは遅くなりますが、おそらく問題ではありません。この変更による直接的な影響を示すグラフは次のとおりです。

Java RSS メモリ使用量グラフ http://lanai.dietpizza.ch/images/stackoverflow-rss-problem-fixed.png

解決する時間があるかどうかわからない質問:

  • JVM 内で何が起こっているのか MappedByteBuffer.force()?何も変更されていない場合でも、ファイル全体が書き込まれるのでしょうか?ファイルの一部ですか?最初にロードしますか?
  • RSS には常に一定量の MBB が含まれていますか?(RSS は、割り当てられた MBB の合計サイズの約半分でした。偶然?そうではないのではないかと思います。)
  • 私が移動させたら MappedByteBuffer.force() 定期的なタイマーではなく、データベース更新イベントを使用した場合、問題は魔法のように解決するのでしょうか?
  • なぜ RSS の傾きはこれほど規則的だったのでしょうか?これは、アプリケーション負荷メトリックのいずれとも相関しません。
役に立ちましたか?

解決

アイデア:NIOバッファーはJVMの外側に配置されます。

編集: 2016年には、@ Lari Hotariコメント[ヒープなどのサイズが安定していてもSun JVMがRSSメモリを消費し続けるのはなぜですか?] 2009年にRHEL4がglibc <! > lt; 2.10(〜2.3)

よろしく。

他のヒント

RSSは、アクティブに使用されているページを表します。Javaの場合、主にヒープ内のライブオブジェクトとJVMの内部データ構造です。使用するオブジェクトの数を減らすか、処理を減らすこと以外は、サイズを減らすためにできることはあまりありません。

あなたの場合、それは問題だとは思いません。グラフは、テキストを書くときの3ギガではなく、3メガの消費を示しています。それは本当に小さく、ページングを引き起こす可能性は低いです。

では、システムで他に何が起こっていますか? Tomcatサーバーがたくさんあり、それぞれが3MのRSSを消費している状況ですか?あなたは多くのGCフラグを投げていますが、それらはプロセスがGCでほとんどの時間を費やしていることを示していますか?同じマシンでデータベースを実行していますか?

コメントに応じて編集

3M RSSサイズについて-はい、Tomcatプロセスには低すぎるように見えました(ボックスをチェックし、89Mでしばらくアクティブではありませんでした)。ただし、必ずしも<!> gt;とは限りません。ヒープサイズです。ヒープサイズのほぼ5倍になるとは思いません(-Xmx640を使用)。最悪の場合は、ヒープサイズ+アプリごとの定数である必要があります。

私はあなたの番号を疑います。そのため、経時的なグラフではなく、次を実行してスナップショットを取得してください(使用しているプロセスIDに7429を置き換えてください)。

ps -p 7429 -o pcpu,cutime,cstime,cmin_flt,cmaj_flt,rss,size,vsize

(上記のps情報のリクエストに合わせて結果をフォーマットできるように、Stuによって編集します:)

[stu@server ~]$ ps -p 12720 -o pcpu,cutime,cstime,cmin_flt,cmaj_flt,rss,size,vsize
%CPU - - - -  RSS SZ  VSZ
28.8 - - - - 3262316 1333832 8725584

これらの数字を後世に説明するために編集

RSSは、前述のように、常駐セットサイズ(物理メモリ内のページ)です。 SZは、プロセスによって書き込み可能なページ数を保持します(コミット料)。マンページでは、この値を<!> quot; very rough <!> quot;と記述しています。 VSZは、プロセスの仮想メモリマップのサイズ(書き込み可能なページと共有ページ)を保持します。

通常、VSZはわずかに<!> gtです。 SZ、そして非常に<!> gt; RSS。この出力は、非常に珍しい状況を示しています。

唯一の解決策がオブジェクトを減らすことである理由の詳細

RSSは、RAMに常駐しているページ(アクティブにアクセスされているページ)の数を表します。 Javaを使用すると、ガベージコレクターはオブジェクトグラフ全体を定期的に調べます。このオブジェクトグラフがヒープスペースの大部分を占める場合、コレクタはヒープ内のすべてのページにアクセスし、それらのすべてのページがメモリ常駐になるように要求します。 GCは、メジャーコレクションごとにヒープを圧縮することに関して非常に優れているため、部分的なヒープで実行している場合、ほとんどのページをRAMに配置する必要はありません。

その他のオプション

数百から数千のスレッドがあることに言及していることに気付きました。これらのスレッドのスタックもRSSに追加されますが、それほど多くはないはずです。スレッドの呼び出し深度が浅いと仮定すると(アプリサーバーハンドラースレッドに一般的)、それぞれにハーフメガのコミットチャージがありますが、各ページで消費するのは物理メモリの1つまたは2つだけです。

なぜこうなった?「ボンネットの下で」何が起こっているのでしょうか?

JVM はヒープだけでなく、より多くのメモリを使用します。たとえば、Java メソッド、スレッド スタック、ネイティブ ハンドルは、JVM 内部データ構造だけでなく、ヒープとは別のメモリに割り当てられます。

あなたのケースでは、次のようなトラブルの原因が考えられます。NIO (前述)、JNI (前述)、過剰なスレッドの作成。

JNIについて、アプリケーションはJNIを使​​用していないと書きましたが...どのような種類の JDBC ドライバーを使用していますか?タイプ2で漏れている可能性はありますか?ただし、データベースの使用率が低いとおっしゃっていたので、その可能性は非常に低いです。

過剰なスレッドの作成については、各スレッドが独自のスタックを取得しますが、これは非常に大きくなる可能性があります。実際、スタック サイズは VM、OS、アーキテクチャによって異なります。のために JRockit Linux x64 では 256K です。Sun のドキュメントには Sun の VM に関する参照が見つかりませんでした。これは、スレッド メモリに直接影響します (スレッド メモリ = スレッド スタック サイズ * スレッド数)。また、大量のスレッドを作成して破棄すると、メモリはおそらく再利用されなくなります。

JVM の実メモリ消費を抑えるにはどうすればよいでしょうか?

正直に言うと、私にとって数百から数千のスレッドは膨大に思えます。とはいえ、本当に多くのスレッドが必要な場合は、スレッド スタック サイズを次のように設定できます。 -Xss オプション。これにより、メモリ消費量が削減される可能性があります。しかし、これで問題がすべて解決するとは思えません。実際のメモリグラフを見ると、どこかにリークがあるのではないかと考えがちです。

現在のJavaのガベージコレクターは、割り当てられたメモリを解放しないことで知られていますが、メモリはもう必要ありません。ただし、ヒープサイズが640MBに制限されているにもかかわらず、RSSサイズが<!> gt; 3GBに増加することは非常に奇妙です。アプリケーションでネイティブコードを使用していますか、またはTomcatのネイティブパフォーマンス最適化パックを有効にしていますか?その場合、もちろん、コードまたはTomcatでネイティブメモリリークが発生する可能性があります。

Java 6u14で、Sunは新しい<!> quot; Garbage-First <!> quot;を導入しました。不要になったメモリをオペレーティングシステムに解放できるガベージコレクタ。まだ実験的として分類されており、デフォルトでは有効になっていませんが、実行可能なオプションである場合は、最新のJava 6リリースにアップグレードし、コマンドライン引数<!> quot; -XXで新しいガベージコレクターを有効にしようとします:+ UnlockExperimentalVMOptions -XX:+ UseG1GC <!> quot;。問題が解決する可能性があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top