Проблема Java map / nio / NFS, вызывающая сбой виртуальной машины: «произошла ошибка в недавней небезопасной операции доступа к памяти в скомпилированном коде Java» - PullRequest
9 голосов
/ 01 июня 2010

Я написал класс парсера для определенного двоичного формата ( nfdump , если кому-то интересно), который использует java.nio's MappedByteBuffer для чтения файлов по несколько ГБ каждый.Двоичный формат - это просто серия заголовков и в основном двоичных записей фиксированного размера, которые передаются вызываемому с помощью вызова nextRecord (), который выдвигает конечный автомат, возвращая ноль, когда это будет сделано.Это хорошо работает.Он работает на компьютере разработчика.

На моем производственном хосте он может работать в течение нескольких минут или часов, но, кажется, всегда выдает «java.lang.InternalError: ошибка произошла в недавней небезопасной операции доступа к памятив скомпилированном коде Java ", перебирая один из методов Map.getInt, getShort, то есть операцию чтения на карте.

Бесспорный (?) код, который устанавливает карту, таков:

    /** Set up the map from the given filename and position */
    protected void open() throws IOException {
            // Set up buffer, is this all the flexibility we'll need?
            channel = new FileInputStream(file).getChannel();    
            MappedByteBuffer map1 = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
            map1.load(); // we want the whole thing, plus seems to reduce frequency of crashes?
            map = map1;
            // assumes the host  writing the files is little-endian (x86), ought to be configurable
            map.order(java.nio.ByteOrder.LITTLE_ENDIAN);
            map.position(position);
    }

и затем я использую различные методы map.get * для чтения шортов, целых, длинных и других последовательностей байтов, прежде чем попасть в конец файла и закрыть карту.

Я никогда невидел исключение, брошенное на мой хост разработки.Но существенная разница между моим рабочим хостом и разработкой заключается в том, что в первом случае я читаю последовательности этих файлов по NFS (вероятно, 6-8 ТБ, в конечном счете, все еще растущий).На моем компьютере разработчика у меня есть небольшой выбор этих файлов локально (60 ГБ), но когда он взрывается на производственном хосте, обычно до того, как он достигает 60 ГБ данных.

Обе машины работают на Java 1.6.0_20-b02, хотя на рабочем хосте запущен Debian / lenny, хостом разработки является Ubuntu / karmic.Я не уверен, что это будет иметь значение.Обе машины имеют 16 ГБ ОЗУ и работают с одинаковыми настройками Java-кучи.

Я считаю, что если в моем коде есть ошибка, в JVM достаточно ошибки, чтобы не выдатьправильное исключение!Но я думаю, что это просто конкретная ошибка реализации JVM из-за взаимодействия между NFS и mmap, возможно повторение 6244515 , которое официально исправлено.

Я уже пытался добавить в «нагрузку»вызов, чтобы заставить MappedByteBuffer загрузить его содержимое в ОЗУ - это, казалось, задержало ошибку в одном тестовом прогоне, который я сделал, но не предотвратило его.Или это могло быть совпадение, которое было самым длинным за всю историю до краха!

Если вы читали это далеко и делали подобные вещи с java.nio раньше, каким был бы ваш инстинкт?Прямо сейчас мое переписать это без nio:)

1 Ответ

4 голосов
/ 02 июня 2010

Я бы переписал его без использования mapped NIO. Если вы имеете дело с более чем одним файлом, существует проблема, заключающаяся в том, что сопоставленная память никогда не освобождается, поэтому вам не хватит виртуальной памяти: NB это не обязательно просто OutOfMemoryError, который взаимодействует с сборщиком мусора, это не удалось выделить новый сопоставленный буфер. Я бы использовал FileChannel.

Сказав это, крупномасштабные операции с файлами NFS всегда чрезвычайно проблематичны. Вам было бы гораздо лучше изменить систему так, чтобы каждый файл читался его локальным процессором. Таким образом, вы получите огромное улучшение скорости, гораздо больше, чем 20%, которые вы потеряете, если не будете использовать отображенные буферы.

...