Короткий ответ,
, ничего не делая, вы можете нажать на текущий предел в 1,5 раза. Это означает, что если вы можете обрабатывать 800 МБ, вы можете обрабатывать 1200 МБ. Это также означает, что если какой-либо трюк с java -Xm ....
вы можете перейти к точке, где ваш текущий код может обрабатывать 7 ГБ, ваша проблема будет решена, потому что фактор 1.5 приведет вас к 10,5 ГБ, если у вас есть это пространство в вашей системе и что JVM может это получить.
Длинный ответ:
ошибки довольно самоописательный. Вы нажимаете ограничение на практическую память в своей конфигурации. Существует много размышлений о пределе, который вы можете иметь с JVM, я недостаточно знаю об этом, так как я не могу найти никакой официальной информации. Тем не менее, вы каким-то образом будете ограничены ограничениями, такими как доступный обмен, использование адресного пространства ядра, фрагментация памяти и т. Д.
Что происходит сейчас, так что объекты ByteArrayOutputStream
создаются с использованием буфера по умолчанию размером 32, если вы это делаете не поставляйте никаких размеров (это ваш случай). Всякий раз, когда вы вызываете метод write
на объекте, запускается внутренняя машина. openjdk implementation release 7u40-b43, который, кажется, идеально подходит для вывода вашей ошибки, использует внутренний метод ensureCapacity
, чтобы проверить, что в буфере достаточно места для размещения байтов, которые вы хотите записать. Если места недостаточно, для увеличения размера буфера вызывается другой внутренний метод grow
. Метод grow
определяет соответствующий размер и вызывает метод copyOf
из класса Arrays
для выполнения задания. Соответствующий размер буфера - это максимальный размер между текущим размером и размером, необходимым для хранения всего содержимого (настоящий контент и новый контент для записи). Метод copyOf
из класса Arrays
(follow the link) выделяет пространство для нового буфера, копирует содержимое старого буфера в новый и возвращает его в grow
.
Ваша проблема возникает при распределении пространства для нового буфера. После некоторого write
вы попали в точку, в которой исчерпана доступная память: java.lang.OutOfMemoryError: Java heap space
.
Если посмотреть в деталь, вы читаете на кусках 2048. Так
- вашей первая запись на выращивает размер буфера от 32 до 2048
- второго вызова удвоит его 2 * 2048
- Ваш третий вызов займет 2^2 * 2048, вы должны время написать еще два раза перед необходимостью выделения.
- , затем 2^3 * 2048, у вас будет время для написания 4 mores, прежде чем выделять снова.
- В какой-то момент ваш буфер будет иметь размер 2^18 * 2048, который составляет 2^19 * 1024 или 2^9 * 2^20 (512 МБ)
- затем 2^19 * 2048, что составляет 1024 МБ или 1 ГБ
Что-то, что неясно в вашем описании, так это то, что вы можете как-то читать до 800 МБ, но не можете выйти за его пределы. Вы должны объяснить это мне.
Я ожидаю, что ваш предел будет ровно силой 2 (или близко, если мы используем мощность 10 единиц). В этой связи я ожидаю, что вы сразу начнете испытывать проблемы над одним из них: 256 МБ, 512 МБ, 1 ГБ, 2 ГБ и т. Д.
Когда вы нажимаете этот предел, это не означает, что вы потеряли память, это просто означает, что нельзя выделить другой буфер в два раза больше размера уже имеющегося буфера. Это наблюдение открывает возможности для улучшения вашей работы: найти максимальный размер буфера, который можно выделить и зарезервировать его авансовый вызвать соответствующий конструктор
ByteArrayOutputStream bArrStream = new ByteArrayOutputStream(myMaxSize);
Это имеет преимущество уменьшения накладного выделения фона памяти, что происходит под капот, чтобы вы были счастливы. Делая это, вы сможете перейти к 1.5 лимиту, который у вас есть прямо сейчас. Это просто потому, что в последний раз, когда буфер был увеличен, он переместился с половины текущего размера на текущий размер, и в какой-то момент у вас в памяти был как текущий буфер, так и старый. Но вы не сможете выйти за пределы 3-х кратного предела, который у вас сейчас есть. Объяснение точно такое же.
Было сказано, что у меня нет никаких волшебных предложений для решения проблемы, кроме обработки ваших данных кусками заданного размера, по одному куску за раз. Другим хорошим подходом будет использование предложения Такахико Кавасаки и использование MappedByteBuffer
. Имейте в виду, что в любом случае вам понадобится не менее 10 ГБ физической памяти или swap-памяти, чтобы иметь возможность загрузить файл размером 10 ГБ.
см
В примере кода, который вы упомянули, вы просто загружаете весь файл в 'ByteArrayOutputStream'. Что такое прецедент? Действительно нужны все данные файла в 'byte []'? – Santosh
Можете ли вы сообщить мне, какую версию JDK вы планируете использовать, у меня есть другое решение для JDK 8 и JDK7 или меньше. – Bhupi
@ Luffy имеет смысл ответить на этот вопрос, не зная ** почему ** столько данных считывается в память? – k3b