2011-12-18 16 views
4

5〜6スレッドを作成するアプリケーションを検討してください。サイクル内の各スレッドは5MBのページサイズにMappedByteBufferを割り当てます。アプリケーションが大きなファイルで動作するときjava.nio.MappedByteBufferを使用するときにOutOfMemoryを防止する

MappedByteBuffer b = ch.map(FileChannel.MapMode.READ_ONLY, r, 1024*1024*5); 

遅かれ早かれ、OOMは

java.io.IOException: Map failed at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:758) 
Caused by: java.lang.OutOfMemoryError: Map failed 
     at sun.nio.ch.FileChannelImpl.map0(Native Method) 
     at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:755) 

が仕様によると、MappedBufferは、すぐにそれがGC自体であるとして、直接メモリを処分しなければならないスローされます。問題はMappedBuffer-sがGC-edされていて遅れてしまった後、直接メモリが終了したように見えます。

このような状況を回避するにはどうすればよいですか?おそらく、MappedBufferを暗黙的に破棄したり、MappedBufferのプールを使用したりして

+0

のために割り当てられることをお勧めしますか? – fge

+1

例外は** NOT OOM **ですが、IOExceptionです。仮想アドレス空間が不足しています。かなり多くのコードを表示する。マップされたバッファとJavaでのレクラメーションは、長時間の問題です(まだ解決していません)。 – bestsss

答えて

2

WeakHashMapMappedBuffersをプールしても問題ありません。

しかし根本原因を推測する前に、すべてのプラグインがインストールされた状態でVisual VM 1.3.3にアプリを接続することをおすすめします。その結果、OOMエラーの原因を正確に把握できます。あなたはこれらのMappedBuffersがそれをやっていると推測していますが、5〜6スレッドではそれぞれ5MBで、合計で25〜30MBです。

推測よりもデータを持つ方がよい。 Visual VMがそれを入手します。

+0

実際には、各スレッドはファイルサイズによって異なります。これは、バッファの総量に制限がないことを意味します。おそらく私はセマフォーを使って膨大な量のメモリーを割り当てるのを防ぐべきです。とにかく私はプロファイラーを試してみます – user12384512

+0

マップされたバッファーをプールすることはできません!それらは、マッピング仮想アドレスによって返されたアドレスをファイル内の場所にカバーし、ページフォルトによってデータをロードする+ファイルハンドルのクローズまたはリクエストに書き戻すように作成されます。 – bestsss

+1

... 'WeakHashMap'はキー自体がバッファーではないので、全く役に立たない/有害なので、' WeakHashMap'が(内部的に)消去される前にGCでも利用できません。問題にGC依存構造を使用することは間違っています。 – bestsss

3

"ヒープスペース"または "permgenスペース"ではなく、 "マップに失敗しました"というエラーメッセージが表示されます。これは、JVMに十分なアドレススペースがないことを意味します。が利用可能です。

this bugをSunのデータベースに、またthis questionを参照してください。

最初のリンクが近くにあり、回避策(ewww)を提供するもの、第2のリンクは言う:

try { 
     buffer = channel.map(READ_ONLY, ofs, n); 
    } catch (java.io.IOException e) { 
     System.gc(); 
     System.runFinalization(); 
     buffer = channel.map(READ_ONLY, ofs, n); 
    } 
+0

GCアルゴリズムとそのパラメータに依存して、 'System.gc()'呼び出しはNOPであるかもしれません。 '-XX:+ DisableExplicitGC'を使うと、OOMをキャッチしてGC要求後に再試行するという醜いパッチを効果的に削除します。 – bestsss

4

あなたが直接マッピングされたバイトバッファをクリーンアップすることにより、GCをトリガすることを避けることができます。

廃棄前にこれを呼び出すと、仮想メモリが不足することはありません。 MappedBufferは処分しなければならない

(多数のファイルを持っていない限り)おそらく、あなたは、あまり頻繁に大きなたByteBufferを作成する時に見ることができるれるMappedByteBufferを作成する無料ではありません(一部のマシンでは約50マイクロ秒かかります)

+0

残念ながら、これらのメソッドは何かをするように見えますが、実際にはByteBufferをマップ解除しません。この方法はダイレクトメモリでのみ機能します。 –

0

GC自体と同じように直ちにメモリに保存してください

実際には私が見ることができるところはありません。 と決して言われていない長いバグパレードアイテムがあります。がリリースされました。

それはこれを言うん:

は、それ故、直接バッファは、あなたのコードは何をやっている主 大、長寿命のバッファ

好奇心
+1

彼らは確かに処分されていますが、主な問題は、小さなJavaオブジェクトがメガバイトへの参照を保持し、GCが仮想メモリをリリースし、ファイナライズ/アンマップすることが急務ではないと考えていることです。 – bestsss

+0

@bestsssどちらかが「廃棄」されているか、そうではありません。あなたの心を作りなさい。 Bug Parade item #4724038を検索する必要があります。それはあなたに同意しない。 – EJP

+1

コメントをもう一度読んでください。それらは処分されるが、あまりにもしばしば遅延される。私はWindowsとLinux/Solarisの両方でバグがよく、ネイティブなインプリメントがあることを知っています。マップされたバッファがマッピングされていないことを示すバグはありません.ByteBuffer(およびそのビュー)がガベージコレクトされた後です。彼らは、通常の方法で強制的に閉じたり、マップ解除したりすることはできません。 SIGSEVを危険にさらしたり、別のファイルや定義されていない結果を使用して、バッファをハックしてマップを解除することは常に可能でした。 – bestsss

関連する問題