Подтвердить что ты не робот

Как работает распределение памяти в ArrayList?

Насколько я знаю, когда мы создаем ArrayList:

ArrayList<String> list = new ArrayList<String>(SIZE);

JVM резервирует для него смежную часть памяти. Когда мы добавляем новые элементы в наш список, когда количество элементов достигает 75% от SIZE, он резервирует новую, смежную часть памяти и копирует все элементы.

Наш список становится все больше и больше. Мы добавляем новые объекты, и список необходимо перестроить еще раз.

Что происходит сейчас?

JVM ищет непрерывный сегмент памяти, но не находит достаточно места.

The Garbage Collector может попытаться удалить некоторые неиспользуемые ссылки и дефрагментацию памяти. Что произойдет, если JVM не сможет зарезервировать место для нового экземпляра списка после этого процесса?

Создает ли он новый, используя максимально возможный сегмент? Какой Exception будет выброшен?

Я прочитал этот вопрос Java: как ArrayList управляет памятью, и один из ответов:

Ссылка не занимает много места. Но в любом случае используется некоторая часть пространства. Когда массив становится все больше, это может быть проблемой. Мы также не можем забывать, что у нас есть другие вещи, которые используют пространство памяти.

4b9b3361

Ответ 1

Если JVM не может выделить запрошенный объем памяти, он будет бросать

OutOfMemoryError

Что это. На самом деле распределение памяти JVM имеет только два возможных результата:

  • Приложению предоставляется запрошенный объем памяти.
  • JVM выбрасывает OutOfMemoryError.

Нет промежуточных параметров, например, выделяется некоторый объем памяти.

Это не имеет никакого отношения к ArrayList, это проблема JVM. Если вы спросите, как ArrayList каким-то образом управляет этой ситуацией особым образом, тогда ответьте "Нет, это не так". Он просто пытается выделить необходимое количество памяти и позволяет JVM думать обо всем остальном.

Ответ 2

В Java ссылки на объект хранятся в непрерывной памяти. Фактические объекты могут оставаться непоследовательными. Таким образом, для вашего массива может быть 10 объектов, JVM нужно зарезервировать память только для ссылок на объекты, а не для объектов. Поэтому, если каждая ссылка принимает байт (приблизительно не правильное значение), но каждый объект занимает КБ, и у вас есть массив из 10 элементов, JVm попытается зарезервировать разную память только 1 * 10 B, т.е. 10 B. объекты могут находиться в 10 разных ячейках памяти на общую сумму 10 КБ. Помните, что и смежные, и несмежные пространства памяти предназначены для памяти, выделенной для потока.

Когда ему нужно изменить размер массива, JVM попытался найти массив contiguos более новой длины. Поэтому, если вы хотите изменить размер массива от 10 до 20 элементов, он попытается зарезервировать смежное пространство в 20 КБ (используя приведенный выше пример). Если он найдет это пространство, он выполнит копию ссылок из старого массива в новый массив. Если он не найдет это пространство, он попытается сделать GC. Если он все еще не находит пробел, он выдает исключение OutofMemoryException.

Следовательно, в любое время, когда вы изменяете размер массива, JVM необходимо найти память contiguos для хранения референций нового массива. Поэтому, если вы хотите расширить массив до размера 1000 элементов, и каждая ссылка является байтом каждый, JVm попытается найти память contiguos размером 1000 * 1 КБ, которая составляет 1 МБ. Если он найдет эту память, она сделает копию ссылок и пометит память прежнего contiguos для GC, всякий раз, когда GC запускается в следующий раз Если он не может найти память, он попытается выполнить GC, и если он все еще не найдет память contiguos, он выкинет исключение из памяти

Это код в ArrayList, который выполняет изменение размера. http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.ensureCapacity%28int%29

Ответ 3

Это вызовет OutOfMemoryError, как только недостаточно места для кучи, чтобы выделить новый массив.

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

Итак, если ваш предел памяти составляет 10 Мбайт, а массив занимает 2 МБ и имеет размер до 3 МБ, а строки занимают 6 МБ, тогда OOM будет выброшен, хотя после этой операции вы будете только имеют 3 + 6 = 9 МБ в памяти. Один из способов избежать этого, если вы хотите, чтобы запустить очень близко к ограничениям памяти с помощью огромного массива, - это размер массива до полного размера, чтобы начать с него, поэтому ему не нужно изменять размер.

Ответ 4

Я предполагаю, что у него закончится память, так как не будет места для использования в случае, когда JVM может расширить размер массива.

Ответ 5

Самое первое, что я хочу исправить: когда мы добавляем новые элементы в наш список, когда количество элементов достигает 100% от SIZE, он резервирует новую, смежную часть памяти и копий все элементы.

Новый размер ArrayList будет:

NewSize of ArrayList = (CurrentSize * 3/2) + 1

Но этот способ никогда не рекомендуется, если у нас есть идея, сколько объектов нужно сохранить, мы можем использовать следующий конструктор ArrayList: -

ArrayList ar = новый ArrayList (int initialCapacity);

Если наша JVM не может указать достаточное смежное пространство в куче для ArrayList, во время выполнения мы получим

  • Ошибка выполнения: OutOfMemoryError