Усеченный файл сопоставленной памяти - PullRequest
12 голосов
/ 13 мая 2011

Я использую 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();
    }
}

При использовании 32-битной Windows или Linux у меня часто возникает проблема с отображением, но в 64-битной LinuxВ производственной среде все работает без предупреждений, но файл сохраняет свой первоначальный размер.

Кто-нибудь может объяснить, почему это происходит и / или как решить проблему?

Ответы [ 2 ]

7 голосов
/ 06 июля 2011

Ваша проблема в том, что вы используете ненадежный метод для закрытия сопоставленного байтового буфера (сто обращений к 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 несовместимым образом (но на самом деле я не верю, что это когда-либо произойдет).

3 голосов
/ 29 мая 2012

Это просто дополнение к предыдущему ответу, которое является полностью правильным.

JDK 1.7 жалуется на использование sun.misc.Cleaner, говоря, что классы в этом пространстве имен не являются формальной частью JDK,и может исчезнуть в будущем.Тем не менее, по состоянию на 1.7 они все еще присутствуют.

Если метод .clean() недоступен, тогда использование System.gc() может использоваться как запасной метод, однако это должно быть признано "взломом" ипоэтому необходимо соблюдать осторожность.

Хотя System.gc() не может заставить закрытое отображение, на которое нет ссылок, на практике это часто приводит к очистке.Опыт работы с 32-битным Linux (и Solaris) показывает, что буферы освобождаются во время каждого теста во время первого или второго вызова System.gc().Тем не менее, поведение в Windows отличается.В большинстве случаев все сопоставления освобождаются к концу второго вызова на System.gc(), но иногда для этого требуется 3 вызова.Есть все еще случаи, когда требуется больше вызовов, с требованием уменьшения количества вызовов по частоте.Это может быть обманчивым, поскольку тесты могут указывать на то, что 4 вызова - это все, что требуется, только для того, чтобы он не прошел через месяц.Тогда 5 вызовов могут казаться адекватными, что может привести к сбою в течение 6 месяцев.

Тестирование на предмет выпуска карты может быть выполнено с помощью блока try/catch вокруг FileChannel.truncate() с циклом дляповторите попытку в случае неудачи.Цикл не может быть бесконечным, поскольку существуют патологические случаи, когда конкретная конфигурация кучи приводит к тому, что сборщик мусора никогда не очищает отображение.Тем не менее, цикл около 10 будет охватывать почти все случаи.Если к этому моменту объект не исчез, то он никуда не денется, и приложение должно будет сдаться.Это может показаться неадекватным, но на практике это крайне маловероятно и будет проблемой только для JVM, которая не поддерживает очистители.

...