Вы определенно НЕ хотите загружать 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. Я очень уверен, что все вышеописанное точно, но если что-то не так, я хочу знать.
300MB в память ??????? – Shoban
Даже в '09 большинство компьютеров имеют более 300 МБ. Теперь его удивительно дешево. Вы можете купить ПК с 16 ГБ за менее чем 1000 долларов США. –