2010-06-01 3 views
9

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

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

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

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

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

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

+0

Я предполагаю, что вы уже видели D8 (http://nfs.sourceforge.net/) – Justin

+0

У меня не было, спасибо, но тогда я тоже не пишу эти файлы. –

+0

Я вижу, что это происходит с файлами с отображением памяти в локальных файловых системах ext4 и tmpfs с Java 7u1. –

ответ

4

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

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

+0

Я действительно думал об отсутствии виртуального адресного пространства, но, как вы сказали, должно проявляться в сбое отображения (плюс я читаю только один файл за раз и в 64-битной системе). Я, вероятно, перестраиваю серверы так, чтобы файлы находились на том же сервере, что и процесс java, и избегайте того, что NFS это проблема. В краткосрочной перспективе я просто прочитаю все это в ByteBuffer, но поскольку несколько потоков читают одни и те же файлы, часто в одно и то же время, это переопределяет материал, который mmap * ought * будет изящным решением! –

+0

Да, я надеялся на ответ, который позволил мне сохранить mmap, мне просто нужно было заставить кого-то сказать «это не сработает» :) Теперь код open() просто читает весь фрагмент выделен ByteBuffer. Хотя мой инстинкт состоял в том, чтобы волноваться из-за потери памяти (как несколько читателей = несколько копий в куче), я не видел падения производительности по сравнению с предыдущими тиражами, поэтому не могу жаловаться. Я оставил старый код в надежде, что смогу восстановить «изящный» mmap, но при условии, что мои файлы nfdump остаются одинаковыми, мне, вероятно, он больше не понадобится. –

+0

«несколько читателей = несколько копий в куче»: только если вы сделаете эти несколько копий. Разве вы не можете организовать какой-то однопользовательский доступ? – EJP