13

インデックスファイルにメモリマップドIOを使用していますが、問題は、ファイルのほとんどが空の場合、ファイルのサイズを変更できないことです。

以前のどこか:

MappedByteBuffer map = raf.getChannel().map(MapMode.READ_WRITE, 0, 1 << 30);
raf.close();
// use map
map.force();
map = null;

サイズ変更:

for (int c = 0; c < 100; c++) {
    RandomAccessFile raf = new RandomAccessFile(indexFile, "rw");
    try {
        raf.setLength(newLen);
        if (c > 0) LOG.warn("used " + c + " iterations to close mapped byte buffer");
        return;
    } catch (Exception e) {
        System.gc();
        Thread.sleep(10);
        System.runFinalization();
        Thread.sleep(10);
    } finally {
        raf.close();
    }
}

WindowsまたはLinux32ビットを使用している場合、マッピング解除の問題が発生することがよくありますが、64ビットLinux実稼働環境では、すべてが警告なしで機能するように見えますが、ファイルは元のサイズを維持します。

なぜこれが起こるのか、そして/または問題を解決する方法を誰かが説明できますか?

4

2 に答える 2

8

あなたの問題は、信頼できない方法を使用してマップされたバイトバッファーを閉じていることです(100回の呼び出しでSystem.gc()System.runFinalization()何も保証されません)。残念ながら、Java API にはこれを行うための信頼できる方法はありませんが、Sun JVM (およびおそらく他のいくつかのもの) では、次のコードを使用できます。

public void unmapMmaped(ByteBuffer buffer) {
  if (buffer instanceof sun.nio.ch.DirectBuffer) {
    sun.misc.Cleaner cleaner = ((sun.nio.ch.DirectBuffer) buffer).cleaner();
    cleaner.clean();
  }
}

もちろん、これは JVM に依存するものであり、Sun が変更sun.nio.ch.DirectBufferまたはsun.misc.Cleaner互換性のない方法を決定した場合は、コードを修正する準備ができている必要があります (しかし、実際には、これが起こるとは思いません)。

于 2011-07-06T18:51:06.313 に答える
3

これは、前の回答に対する単なる補足であり、完全に正しいです。

sun.misc.CleanerJDK 1.7は、この名前空間のクラスは JDK の正式な部分ではなく、将来消える可能性があると言って、 の使用について不満を述べています。ただし、1.7 の時点ではまだ存在しています。

.clean()メソッドが利用できない場合はSystem.gc()、フォールバック メソッドとして using を使用できますが、これは「ハック」であることを認識し、注意が必要です。

参照されていないマッピングを強制的に閉じることはできませSystem.gc()んが、実際にはクリーンアップが発生することがよくあります。32 ビット Linux (および Solaris) での経験では、すべてのテストで、最初または 2 回目のSystem.gc(). ただし、Windows での動作は異なります。ほとんどの場合、すべてのマッピングは への 2 回目の呼び出しの終わりまでに解放されますがSystem.gc()、3 回の呼び出しが必要になる場合もあります。より多くの呼び出しが必要な場合もあり、より多くの呼び出しが必要になる頻度は減少します。テストでは 4 回の呼び出しで十分であることが示され、1 か月後に失敗する可能性があるため、これは誤解を招く可能性があります。5 回の呼び出しで十分に思えるかもしれませんが、6 か月で失敗するだけです。

マップが解放されたかどうかを確認するテストは、失敗時に操作を再試行するループを使用してtry/catchブロックを使用して実行できます。FileChannel.truncate()特定のヒープ構成が原因でガベージ コレクターがマッピングをクリーンアップしないという異常なケースがあるため、ループを無限にすることはできません。ただし、約 10 のループでほぼすべてのケースがカバーされます。その時点までにオブジェクトがなくなっていない場合、オブジェクトはどこにも行かず、アプリケーションはあきらめなければなりません。これは不十分に思えるかもしれませんが、実際には非常にまれであり、クリーナーをサポートしていない JVM でのみ問題になります。

于 2012-05-29T15:24:35.720 に答える