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

C++ выделяющий массив не смежно

Рассмотрим этот код С++ как пример.

int *A = new int [5];
int *B = new int [5];
int *C = new int [5];
delete []A;
delete []C;
int *D = new int [10];

Очевидно, что любая машина может обрабатывать этот случай без каких-либо проблем с переполнением буфера или утечкой памяти. Однако представьте, что длины умножаются на миллион или даже большее число. Насколько я знаю, адреса (по крайней мере, виртуальные адреса) всех элементов массива являются последовательными. Поэтому всякий раз, когда я создаю массив, я могу быть уверен, что они являются смежными кусками в виртуальной памяти, и я могу выполнить арифметику указателей для доступа к n-му элементу, если у меня есть указатель на первый. Мой вопрос проиллюстрирован на следующем рисунке (регистры, представляющие конец массива, для простоты игнорируются). распределение массива кучи

После выделения A, B, C в куче мы освобождаем A и C и получаем два свободных фрагмента памяти длиной 5 (помечены зелеными точками). Что произойдет, когда я хочу выделить массив длиной 10? Я думаю, что есть 3 возможных случая.

  • Я получаю исключение bad_alloc для не имеющего смежного 10-портового блока памяти.
  • Программа автоматически перераспределяет массив B в начало кучи и объединяет остальную неиспользуемую память.
  • Массив D будет разделен на 2 части и сохранен не соприкасаемо, что приведет к не постоянному времени доступа для n-го элемента массива (если имеется гораздо больше двух разделов, он начинает напоминать связанный список, а не массив).

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

4b9b3361

Ответ 1

Я получаю исключение bad_alloc для не имеющего смежного 10-портового блока памяти.

Это может произойти.

Программа автоматически перераспределяет массив B в начало кучи и объединяет остальную неиспользуемую память.

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

Массив D будет разделен на 2 части и сохранен не смежно, вызывая не постоянное время доступа для n-го элемента массива (если есть намного больше двух разделов, он начинает напоминать связанный список, а не массив).

Этого также не может быть. В С++ элементы массива хранятся смежно, так что возможна арифметика указателя.

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

В качестве примера рассмотрим машину с памятью объемом 8 ГБ (2 ^ 33 байта) с 64-разрядной ОС на 64-битном процессоре. Адреса, выделенные программе, не дают всего меньше 8 ГБ; он может получить мегабайтный кусок памяти по адресу 0x00000000ffff0000 и еще один мегабайтный кусок по адресу 0x0000ffffffff0000. Общий объем памяти, выделенной для программы, не может превышать 2 ^ 33 байта, но каждый фрагмент может быть расположен в любом месте в пространстве 2 ^ 64. (На самом деле это немного сложнее, но достаточно похоже на то, что я описываю).

В вашей картине у вас есть 15 маленьких квадратов, которые представляют куски памяти. Скажем, это физическая память. Виртуальная память - 15 000 маленьких квадратов, из которых вы можете использовать любые 15 в любой момент времени.

Таким образом, учитывая этот факт, возможны и следующие сценарии.

  • Часть виртуального адресного пространства предоставляется программе, которая не поддерживается реальной физической памятью. Когда и если программа пытается получить доступ к этому пространству, ОС попытается выделить физическую память и сопоставить ее с соответствующим адресом, чтобы программа могла продолжить. Если эта попытка не удалась, программа может быть убита ОС. Новая свободная память теперь доступна для других программ, которые могут ее захотеть.
  • Два коротких фрагмента памяти отображаются на новые виртуальные адреса, так что они образуют один длинный непрерывный фрагмент в виртуальной памяти. Помните, что, как правило, существует гораздо больше адресов виртуальной памяти, чем есть физическая память, и обычно легко найти неназначенный диапазон. Как правило, этот сценарий реализуется только тогда, когда рассматриваемые фрагменты памяти являются большими.

Ответ 2

Проблема, о которой вы просите, называется куче-фрагментацией, и это настоящая трудная проблема.

Я получаю исключение bad_alloc для не имеющего смежного 10-портового блока памяти.

Это теория. Но такая ситуация действительно возможна только в 32-битном процессе; 64-разрядное адресное пространство обширно.

То есть, с 64-битным процессом, более вероятно, что фрагментация кучи останавливает реализацию вашей реализации new от повторного использования некоторой памяти, что приводит к условию отсутствия памяти, так как ему нужно задать ядро ​​для нового памяти для всего массива D вместо половины. Кроме того, такое условие OOM скорее приведет к тому, что ваш процесс будет запущен OOM-killer когда-нибудь, когда вы попытаетесь получить доступ к местоположению в D, а не new, бросая исключение, потому что ядро ​​не реализует что он переубедил свою память, пока не стало слишком поздно. Для получения дополнительной информации "перекомпоновка памяти" Google.

Программа автоматически перераспределяет массив B в начало кучи и объединяет остальную неиспользуемую память.

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

Массив D будет разделен на 2 части и сохранен не смежно, вызывая не постоянное время доступа для n-го элемента массива (если есть намного больше двух разделов, он начинает напоминать связанный список, а не массив).

Это также невозможно, так как С++ гарантирует непрерывное хранение массивов (чтобы разрешить доступ к массиву через арифметику указателя).