2009-06-12 3 views
0

Моя задача состоит в том, чтобы открыть большой файл в READ & режиме WRITE и мне нужно искать какую-то часть текста в этом файле путем поиска начальной и конечной точки. Затем мне нужно написать искомую область текста в новый файл и удалить эту часть из исходного файла.исключение при чтения очень больших файлов> 300 MB

Вышеупомянутый процесс я сделаю больше раз. Так что я думал, что для этого процесса будет легко загрузить файл в память CharBuffer и может легко искать по MATCHER класс. Но им получать HeapSpace исключения при чтении, хотя я увеличена до 900Мб, выполнив, как показано ниже Java -Xms128m -Xmx900m readLargeFile Моего кода

FileChannel fc = new FileInputStream(fFile).getChannel(); 
CharBuffer chrBuff = Charset.forName("8859_1").newDecoder().decode(fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size())); 

Для этого выше коды каждого предположил, что его плохая идея до загружает все в память, и если размер файла составляет 300 МБ, это будет 600MB из-за charSet.

Так выше моя задача, то теперь мне предложить некоторые эффективные пути. Обратите внимание, что мой размер файла будет больше и с использованием JAVA только я должен делать все это.

Спасибо заранее ...

+0

300MB в память ??????? – Shoban

+0

Даже в '09 большинство компьютеров имеют более 300 МБ. Теперь его удивительно дешево. Вы можете купить ПК с 16 ГБ за менее чем 1000 долларов США. –

ответ

2

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

Но если шаблон поиска соответствует нескольким строкам, то вам нужно сообщить нам, потому что поиск по строкам не будет работать.

4

Вы определенно НЕ хотите загружать 300 МБ-файл в один большой буфер с Java. Предполагается, что вы делаете что-то более эффективное для больших файлов, чем обычный режим ввода-вывода, но когда вы запускаете Matcher против всего файла, отображаемого в память, как вы, вы можете очень легко вывести память.

Во-первых, память вашего кода отображает файл в память ... это будет потреблять 300 мегабайт памяти в вашем виртуальном адресном пространстве, так как файл находится в нем, хотя это находится вне кучи. (Обратите внимание, что виртуальное адресное пространство размером 300 мегабайт привязано к до тех пор, пока MappedByteBuffer не будет собрано мусором. См. Ниже для обсуждения. JavaDoc for map предупреждает вас об этом.) Затем вы создаете ByteBuffer, поддерживаемый этим mmap ed файлом. Это должно быть хорошо, так как это просто «вид» файла edmmap и, следовательно, он должен иметь минимальную дополнительную память. Это будет маленький объект в куче с «указателем» на большой объект вне кучи. Затем вы декодируете это в CharBuffer, что означает, что вы делаете копию из буфера 300 МБ, но вы делаете копию 600 МБ (в куче), потому что char составляет 2 байта.

Чтобы ответить на комментарий, и глядя на Исходный код JDK, чтобы быть уверенным, когда вы звоните map() как ФП, вы на самом деле карта файл весь в память. Глядя на openJDK 6 b14 собственный код Windows sun.nio.ch.FileChannelImpl.c, он сначала вызывает CreateFileMapping, затем звонит MapViewOfFile. Рассматривая этот источник, если вы попросите отобразить весь файл в память, этот метод будет действовать точно так же, как вы просите. Процитировать MSDN:

Отображение файла делает указанную часть файла видимой в адресном пространстве вызывающего процесса.

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

Способ, которым OP является называть карту, «указанная часть» файла - это весь файл. Это не будет способствовать исчерпанию кучи, но это может способствовать исчерпанию виртуального адресного пространства, которое по-прежнему является ошибкой OOM. Это может убить ваше приложение так же тщательно, как и из кучи.

Наконец, когда вы делаете Matcher, Matcher потенциально делает больше копий этого 600 МБ CharBuffer, в зависимости от того, как вы его используете. Уч. Это небольшая память, используемая небольшим количеством объектов! Учитывая Matcher, каждый раз, когда вы звонитеtoMatchResult(), вы будете делать String копию всегоCharBuffer. Кроме того, каждый раз, когда вы звоните в replaceAll(), в лучшем случае вы сделаете копию String полностью CharBuffer. В худшем случае вы сделаете StringBuffer, который будет медленно расширяться до полного размера результата replaceAll (применяя много давления памяти на кучу), а затем сделайте String.

Таким образом, если вы звоните replaceAll на Matcher против 300 Мб файла с, и ваш матч будет найден, то вы будете первым сделать серию когда-либо больших StringBuffer с пока вы не получите тот, который 600 МБ. Затем вы сделаете копию String этого StringBuffer. Это может быстро и легко привести к истощению кучи.

Вот так: Matcher s не оптимизированы для работы на очень больших буферах. Вы можете очень легко и без планирования сделать несколько очень больших объектов. Я обнаружил это, когда делал что-то подобное достаточно тому, что вы делаете, и сталкивался с исчерпанием памяти, а затем смотрел исходный код для Matcher.

ПРИМЕЧАНИЕ: Существует не unmap звонок. Как только вы вызываете map, виртуальное адресное пространство вне кучи, связанное с MappedByteBuffer, застревает там до тех пор, пока MappedByteBuffer не будет собрано мусором. В результате вы не сможете выполнить определенные операции над файлом (удалить, переименовать, ...), пока MappedByteBuffer не будет собран. Если на разных файлах достаточно много колл-карт, но у них нет достаточного давления памяти в куче, чтобы принудительно собрать мусор, вы можете потерять память за пределами кучи. Для обсуждения см. Bug 4724038.

В результате все обсуждения выше, если вы будете использовать его, чтобы сделать Matcher на больших файлов, и вы будете использовать replaceAll на Matcher, то отображенные на память ввода/вывода, вероятно, не способ идти. Он просто создает слишком много больших объектов в куче, а также использует много виртуального адресного пространства вне кучи. В 32-разрядной версии Windows у вас есть только 2 ГБ (или если вы изменили настройки на 3 ГБ) виртуального адресного пространства для JVM, и это будет оказывать значительное давление на память как внутри, так и вне кучи.

Прошу прощения за длину этого ответа, но я хотел быть основательным. Если вы считаете, что какая-либо часть вышеуказанного неверна, прокомментируйте и скажите это. I не будет do ответный downvotes. Я очень уверен, что все вышеописанное точно, но если что-то не так, я хочу знать.

+0

Следует отметить, что FileChannel.map() возвращает буфер прямого байта. Хотя «да», он отображается, он не обязательно загружается в память и, следовательно, не способствует возникновению каких-либо проблем с исчерпанием памяти, а не части вашего требования 1,5 ГБ. –

+0

@Stu Thompson: посмотрите исходный код JDK. Он фактически загружается в память - по крайней мере, изначально в виртуальное адресное пространство процесса, и по мере его доступа он, безусловно, занимает реальную память. Однако это, скорее всего, вне кучи. – Eddie

0

Претензии, что FileChannel.map загрузит весь файл в память, являются неисправными со ссылкой на MappedByteBuffer, которые возвращает FileChannel.map(). Это «буфер прямого байта», он не исчерпывает вашу память. (Прямые байтовые буферы используют подсистему виртуальной памяти ОС для загрузки и вывоза данных из памяти по мере необходимости, что позволяет обращаться к гораздо более крупным фрагментам памяти, поскольку они являются физическим ОЗУ .) Но опять же, один MBB будет работать только для файлов до ~ 2 ГБ.

Попробуйте это:

FileChannel fc = new FileInputStream(fFile).getChannel(); 
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); 

CharBuffer chrBuff = mbb.asCharBuffer(); 

Он не будет загружать весь файл в память, а chrBuff лишь вид из каркасного MappedByteBuffer, а не копия.

Я не уверен, как обрабатывать декодирование.

+0

Ты не совсем прав. Посмотрите исходный код JDK. Весь файл будет отображаться в виртуальном адресном пространстве процесса Java. В 32-разрядной версии Windows вы можете иметь до 2 ГБ виртуального адресного пространства, если вы не внесли некоторые изменения. Если вы выделяете большую кучу, вы оставляете меньше места для вещей вне кучи, и вы можете легко столкнуться с проблемами. Мы не знаем, что еще делает программа OP, и какие другие собственные ресурсы выделяются вне кучи. – Eddie

+0

Кроме того, ваше предложение не будет работать вообще. Когда вы вызываете mbb.asCharBuffer(), вы не выполняете никакого декодирования. Вы просто рассматриваете ByteBuffer как CharBuffer из половины числа символов. То есть, когда вы получите первый символ, он будет читать ДВУХ БАЙТОВ из ByteBuffer и рассматривать их как один символ. Это будет работать только в том случае, если в рассматриваемом файле используется 2-байтная схема кодирования, что НЕ относится к ISO-8859-1, кодировке, используемой OP. Посмотрите исходный код и прочитайте MSDN. Это не делает то, что вы думали. – Eddie

+0

Кстати, причина, по которой один MBB будет работать только для файлов объемом до 2 ГБ, заключается в том, что это ограничение вашего виртуального адресного пространства под окном по умолчанию. Отображенный файл абсолютно отображается в ваше виртуальное адресное пространство, но вне кучи. Также обратите внимание, что вызов unmap() отсутствует. Вам нужно подождать, пока DirectByteBuffer будет собран в мусор. См. Http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038 – Eddie

-1

В моем случае, добавив -Djava.compiler=NONE после того, как путь к классам может решить проблему.

0

Использование буфера для чтения большую часть файла один раз Существует одна хитрость: каждый раз, когда вы читаете новую строку в буфер, убедитесь, что он имеет перекрытие длины л, что длина подстроки л = длина (подстрока); while (not eof) do начало если find (buffer, substring) return TRUE;
buffer [0..l] = подстрока; буфер [l + 1, end] = read_new_chars_intobuffer; конец

Смежные вопросы