Как бесплатно узнать, сколько освободить? - PullRequest
338 голосов
/ 05 октября 2009

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

Ответы [ 10 ]

311 голосов
/ 05 октября 2009

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

Когда вы звоните free(), он просто просматривает дополнительную информацию, чтобы узнать, насколько велик блок.

126 голосов
/ 21 июня 2010

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

Один типичный способ (in-line) - фактически выделить как заголовок, так и запрошенную вами память, дополненную до некоторого минимального размера. Так, например, если вы запросили 20 байтов, система может выделить 48-байтовый блок:

  • 16-байтовый заголовок, содержащий размер, специальный маркер, контрольную сумму, указатели на следующий / предыдущий блок и т. Д.
  • Область данных 32 байта (ваши 20 байтов дополняются до кратного 16).

Адрес, который вам дается, является адресом области данных. Затем, когда вы освободите блок, free просто возьмет адрес, который вы ему дали, и, предположив, что вы не заполнили этот адрес или память вокруг него, проверите учетную информацию непосредственно перед ней. Графически это будет выглядеть так:

 ____ The allocated block ____
/                             \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
          ^
          |
          +-- The address you are given

Имейте в виду, что размер заголовка и отступа полностью определяются реализацией (на самом деле, все это определяется реализацией (a) , но встроенный вариант учета является распространенным).

Контрольные суммы и специальные маркеры, которые существуют в учетной информации, часто являются причиной ошибок, таких как «Арена памяти повреждена» или «Двойное освобождение», если вы перезаписываете их или освобождаете их дважды.

Заполнение (чтобы сделать выделение более эффективным) - вот почему вы иногда можете написать немного за пределами запрошенного пространства, не вызывая проблем (тем не менее, не делайте этого, это неопределенное поведение и просто потому, что иногда оно работает , не значит, что это нормально)


(a) Я написал реализации malloc во встроенных системах, где вы получили 128 байтов независимо от того, что вы просили (это был размер самой большой структуры в системе), предполагая Вы запросили 128 байтов или меньше (запросы на большее будут удовлетворяться с возвращаемым значением NULL). Очень простая битовая маска (т. Е. Не встроенная) была использована для определения того, был ли выделен фрагмент размером 128 байт.

У других, которые я разработал, были разные пулы для 16-байтовых чанков, 64-байтовых чанков, 256-байтовых чанков и 1K-чанков, опять же с помощью битовой маски, чтобы определить, какие блоки были использованы или доступны.

Обе эти опции позволили сократить накладные расходы на учетную информацию и увеличить скорость malloc и free (нет необходимости объединять смежные блоки при освобождении), что особенно важно в среде, в которой мы работали.

46 голосов
/ 05 октября 2009

Из списка comp.lang.c FAQ: Как free знает, сколько байтов нужно освободить?

Реализация malloc / free запоминает размер каждого блока по мере его выделения, поэтому нет необходимости напоминать о размере при освобождении. (Как правило, размер хранится рядом с выделенным блоком, поэтому вещи обычно ломаются, если границы выделенного блока даже немного превышены)

6 голосов
/ 26 декабря 2009

Этот ответ перенесен из Как free () узнает, сколько памяти нужно освободить? , где я был резко лишен возможности ответить на кажущийся дублирующий вопрос. Этот ответ должен соответствовать этому дубликату:


В случае malloc распределитель кучи сохраняет отображение исходного возвращенного указателя на соответствующие детали, необходимые для free последующего использования памяти. Обычно это включает в себя сохранение размера области памяти в любой форме, относящейся к используемому распределителю, например, необработанный размер, или узел в двоичном дереве, используемом для отслеживания распределений, или количество используемых «единиц памяти». *

free не потерпит неудачу, если вы «переименуете» указатель или скопируете его любым способом. Однако ссылка не учитывается, и только первый free будет верным. Дополнительные free s являются ошибками "double free".

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

4 голосов
/ 05 октября 2009

В связанной заметке Библиотека GLib имеет функции выделения памяти, которые не сохраняют неявный размер, а затем вы просто передаете параметр size в free. Это может устранить часть накладных расходов.

3 голосов
/ 05 октября 2009

malloc() и free() зависят от системы / компилятора, поэтому сложно дать конкретный ответ.

Дополнительная информация по этому другому вопросу .

2 голосов
/ 04 мая 2013

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

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

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

В общем случае ответ таков: для сохранения состояния выделяется отдельная структура данных.

2 голосов
/ 05 октября 2009

Менеджер кучи хранил объем памяти, принадлежащий выделенному блоку, когда вы вызывали malloc.

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

1 голос
/ 05 октября 2009

Чтобы ответить на вторую половину вашего вопроса: да, вы можете, и довольно распространенный шаблон в C следующий:

typedef struct {
    size_t numElements
    int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;

#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;
0 голосов
/ 18 июня 2013

Когда мы вызываем malloc, он просто потребляет больше байта из своего требования. Это более байтовое потребление содержит информацию, такую ​​как контрольная сумма, размер и другую дополнительную информацию. Когда мы звоним бесплатно в это время, он сразу переходит к той дополнительной информации, где находит адрес, а также определяет, сколько блоков будет свободно.

...