Звонок бесплатный или удаление когда-либо освобождает память обратно в «систему» - PullRequest
37 голосов
/ 14 сентября 2009

Вот мой вопрос: освобождает ли вызов или удаляет когда-либо память обратно в «систему». Под системой я подразумеваю, сокращает ли она когда-либо сегмент данных процесса?

Давайте рассмотрим распределитель памяти в Linux, т.е. ptmalloc.

Из того, что я знаю (пожалуйста, исправьте меня, если я ошибаюсь), ptmalloc поддерживает свободный список блоков памяти, и когда приходит запрос на выделение памяти, он пытается выделить блок памяти из этого свободного списка (я знаю, allocator намного более сложен, чем это, но я просто выражаю это простыми словами) Если, однако, он терпит неудачу, он получает память из системы, используя системные вызовы, скажем, sbrk или brk. Когда память свободна, этот блок помещается в свободный список.

Теперь рассмотрим этот сценарий, при пиковой нагрузке много объектов было выделено в куче. Теперь, когда нагрузка уменьшается, объекты свободны. Итак, мой вопрос: как только объект освободится, распределитель выполнит некоторые вычисления, чтобы определить, должен ли он просто сохранить этот объект в списке свободных или в зависимости от текущего размера списка свободных, он может решить вернуть эту память обратно. система, т.е. уменьшить сегмент данных процесса, используя sbrk или brk.

Документация по glibc говорит мне, что если запрос на выделение намного больше, чем размер страницы, он будет распределен с помощью mmap и будет сразу же возвращен в систему после освобождения. Здорово. Но допустим, я никогда не просил о выделении размера, превышающего, скажем, 50 байтов, и я спрашиваю много таких 50-байтовых объектов при пиковой нагрузке в системе. Тогда что?

Из того, что я знаю (поправьте меня, пожалуйста), память, выделенная с помощью malloc, никогда не будет возвращена в систему до тех пор, пока процесс не завершится, т.е. распределитель просто сохранит его в свободном списке, если я его освобожу. Но вопрос, который меня беспокоит, заключается в том, что, если я использую инструмент для просмотра использования памяти моим процессом (я использую pmap в Linux, что вы, ребята, используете?), Он всегда должен показывать память, используемую при пиковой нагрузке ( как память никогда не возвращается обратно в систему, кроме случаев, когда она выделяется с помощью mmap)? То есть память, используемая процессом, никогда не должна уменьшаться (кроме стековой памяти)? Это 1011 *

Я знаю, что что-то упустил, поэтому, пожалуйста, пролите немного света на все это.

Эксперты, пожалуйста, проясните мои концепции относительно этого. Буду благодарен Я надеюсь, что смог объяснить свой вопрос.

Ответы [ 8 ]

12 голосов
/ 14 сентября 2009

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

Для вашей конкретной реализации нет причин для освобождения (), поскольку malloc не освобождает системную память и поэтому освобождает память только для вашего собственного распределителя.

Другая причина использования пользовательского распределителя состоит в том, что вы можете размещать много объектов одинакового размера (т.е. у вас есть некоторая структура данных, которую вы выделяете много). Возможно, вы захотите сохранить отдельный список свободных для этого типа объекта, и освободить / выделить только из этого специального списка. Преимущество этого состоит в том, что это позволит избежать фрагментации памяти.

5 голосов
/ 14 сентября 2009

Это полностью зависит от реализации. В Windows VC ++ программы могут возвращать память обратно в систему, если соответствующие страницы памяти содержат только свободные блоки.

4 голосов
/ 28 ноября 2012

номер


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

  • Увеличивает внутреннюю фрагментацию и, следовательно, может фактически тратить память. (Вы можете вернуть только выровненные страницы в ОС, поэтому вытащив выровненные страницы из блока, вы получите два гарантированно маленьких блока - в любом случае, меньше, чем страница - по обе стороны блока. Если это случается так, что в итоге вы получаете один и тот же общий объем выделенной памяти и множество бесполезных маленьких блоков.)

  • Требуется вызов ядра , и вызовы ядра медленные, поэтому это приведет к замедлению работы программы. Гораздо быстрее просто выбросить блок обратно в кучу.

  • Почти каждая программа либо будет сходиться с установившимся объемом памяти , либо будет увеличиваться до выхода. (Или почти до выхода.) Поэтому вся дополнительная обработка, необходимая для механизма возврата страниц, будет полностью потрачена впустую.

4 голосов
/ 14 сентября 2009

Я думаю, что у вас есть вся информация, необходимая для ответа на ваш собственный вопрос. pmap показывает память, которая в данный момент используется процессом. Итак, если вы вызываете pmap до того, как процесс достигнет пика памяти, то нет, он не покажет пика памяти. если вы вызываете pmap непосредственно перед выходом из процесса, он покажет пиковую память для процесса, который не использует mmap. Если процесс использует mmap, то если вы вызовете pmap в точке, где используется максимальная память, он покажет пиковое использование памяти, но эта точка может не быть в конце процесса (это может произойти где угодно).

Это относится только к вашей текущей системе (т. Е. На основе документации, которую вы предоставили бесплатно, а также mmap и malloc), но, как было сказано предыдущим автором, их поведение зависит от имплементации.

3 голосов
/ 14 сентября 2009

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

Допустим, вы делаете что-то вроде этого:

void *pointers[10000];
for(i = 0; i < 10000; i++)
    pointers[i] = malloc(1024);
for(i = 0; i < 9999; i++)
    free(pointers[i]);

Единственная часть кучи, которую можно безопасно вернуть в систему, - это «кусок дикой природы», который находится в конце кучи. Это может быть возвращено в систему с помощью другого системного вызова sbrk, и распределитель памяти glibc сделает это, когда размер этого последнего чанка превысит некоторый порог.

Вышеуказанная программа будет делать 10000 небольших выделений, но освободит только первые 9999 из них. Последний должен (предполагая, что ничто иное не называло malloc, что маловероятно) должен сидеть прямо в конце кучи. Это не позволит распределителю вообще возвращать память в систему.

Если вы освободите оставшееся выделение, реализация malloc для glibc сможет вернуть большинство страниц, выделенных системе.

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

3 голосов
/ 14 сентября 2009

Это немного отличается от реализации к реализации.

Думайте о своей памяти как о массивном длинном блоке, когда вы выделяете ее, вы забираете немного памяти (помечены '1' ниже):

111

Если я выделю больше памяти с помощью malloc, он получит часть из системы:

1112222

Если я сейчас освобождаю '1':

___2222

Он не будет возвращен в систему, потому что перед ним два (и память дана как непрерывный блок). Однако, если конец памяти освобождается, то эта память возвращается в систему. Если бы я освободил «2» вместо «1». Я бы получил:

111

бит, где было 2, будет возвращен в систему. Основное преимущество освобождения памяти заключается в том, что этот бит можно перераспределить, а не получать больше памяти из системы. например:

33_2222
1 голос
/ 28 сентября 2009

Многие менеджеры памяти могут выполнять операции TRIM, когда они возвращают полностью неиспользуемые блоки памяти в ОС. Однако, как уже упоминалось в нескольких постах, это полностью зависит от реализации.

Но допустим, я никогда не просил о выделении размера, превышающего, скажем, 50 байтов, и я спрашиваю много таких 50-байтовых объектов при пиковой нагрузке в системе. Тогда что?

Это зависит от вашей схемы распределения. Вы освобождаете ALL от небольших ассигнований? Если это так и если диспетчер памяти имеет обработку для выделения небольших блоков, то это может быть возможно. Однако, если вы выделяете много мелких элементов, а затем освобождаете только все, кроме нескольких разбросанных элементов, вы можете фрагментировать память и делать невозможным TRIM-блоки, поскольку каждый блок будет иметь только несколько распределенных распределений. В этом случае вам может потребоваться использовать другую схему распределения для временных и постоянных распределений, чтобы вы могли вернуть временные выделения обратно в ОС.

1 голос
/ 14 сентября 2009

Вот некоторые «преимущества» никогда не высвобождать память обратно в систему:

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