私は、メモリ マップされたファイルを介してアクセスできる小さくてシンプルなストレージ システムを使用しています。2GB を超えるスペースに対処する必要があるため、2GB のような固定サイズの MappedByteBuffer のリストが必要です (さまざまな理由で使用量を減らしています)。次に、すべてが比較的単純です。バッファは1GBなどの特定のスペースにマップされ、さらに必要な場合は新しいMappedByteBufferをマップし(ファイルは自動的に増加します)、さらに必要な場合は3番目のバッファがマップされます。これはうまくいきました。
しかし、その後、ファイルの長さを変更すると問題が発生する可能性があることをJava NIO の本で読みました。
MappedByteBuffer は、関連付けられているディスク ファイルを直接反映します。マッピングが有効な間にファイルが構造的に変更されると、奇妙な動作が発生する可能性があります (正確な動作は OS とファイル システムに依存します)。具体的には、マッピングが有効な間にファイルのサイズが変更された場合、バッファーの一部またはすべてにアクセスできなくなったり、未定義のデータが返されたり、未チェックの例外がスローされたりする可能性があります。ファイルがメモリ マップされている場合、他のスレッドまたは外部プロセスによってファイルがどのように操作されるかに注意してください。
ファイルが増加しているときにOSがファイルを移動し、MappedByteBuffersが無効なスペースを指す可能性があるため、問題が発生する可能性があると思います(または、これを誤解していますか?)
そのため、新しい MappedByteBuffer をリストに追加する代わりに、次のことを行っています
- ファイルの長さを増やす
- バッファ リストをクリアします (古いバッファを破棄し、ガベージ コレクタを介してバッファが解放されることを期待します。うーん、おそらく、cleaner.clean() を介して明示的にすべてのバッファをクリーンアップする必要がありますか?)
- 再マッピング (リストを新しいバッファーで埋める)
しかし、この手順には、マッピング中に時々失敗するという欠点があります
IOException: Operation not permitted
at sun.nio.ch.FileChannelImpl.map0(Native Method)
at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:734)
どうして?バッファリストをクリアしてもバッファが適切に解放および消去されず、複数のマッピングが許可されていないためですか? 本のコメントを無視して、古い作業方法に固執する必要がありますか?
アップデート
- 32 ビット OS でマッピングを分割すると、空き領域を見つけやすくなり、エラーが発生しにくくなるという利点があります ( ref )
- mmap の設定にはコストがかかる可能性があるため、マッピングを小さな部分に分割することは利点です ( ref ) 。
- どちらのアプローチもクリーンではありませんが、2 番目のアプローチは機能するはずですが、マップを解除する必要があります (通常の cleaner.clean ハックで強制的にリリースしようとします)。最初のアプローチは、ファイルサイズを増やすことができるシステム(ibmなど)で機能するはずですが、正確な理由はまだわかりませんが、一般的には機能しません...
- 最もクリーンな方法は、私が恐れている複数のファイルを使用することです(MappedByteBufferごとに1つのファイル)