Она производит освобождение участка памяти, начиная с адреса, передаваемого ей в первом параметре (ссылке или указателе P) и имеющего размер, определяемый вторым параметром (Size). При использовании процедуры FreeMem нельзя забывать, что размер освобождаемого блока должен точно соответствовать размеру, заданному при его размещении посредством GetMem или New. В противном случае либо возникнут потерянные байты, если размер блока при освобождении оказался меньше (а это мусор в памяти), либо в дальнейшем возможна потеря части данных, непосредственно примыкавших к этой области, если размер освобождаемого блока больше ранее отведенного. Последнее чревато особо неприятными последствиями.
Вызовы FreeMem, как и Dispose, в идеале должны быть парны вызовам GetMem. Хотя на практике можно использовать FreeMem вместо Dispose.
Значение ссылочной переменной P после вызова FreeMem считается неопределенным, и ссылаться на P^ в этом случае не стоит.
Если сразу за операторами Dispose или FreeMem следуют конец всей программы или оператор Halt и ему подобные, то можно, в принципе, исключить из программы эти последние процедуры освобождения. Это не совсем по правилам, но может доставить немного радости тем, кто вечно воюет с размером собственных программ. Все сказанное в этом абзаце не относится к резидентным программам.
11.5.3. Процедуры Mark и Release
Механизм действия этих процедур следующий. Пусть переменная P имеет предопределенный тип Pointer, а P1, P2, P3 и P4 объявлены как ссылочные переменные. Пусть текст программы содержит фрагмент
- 203 -
...
New(P1);
New(P2);
Mark(P); { вызов Mark }
New(P3);
New(P4);
...
Release(P); { вызов Release }
...
Перед вызовом процедуры Release куча будет иметь вид, как на рис. 11.4.
Рис. 11. 4
При вызове процедуры Mark в переменную P записалось значение HeapPtr, которое было сразу после размещения P2. Далее были размещены P3 и P4, и указатель HeapPtr передвинулся туда, где он изображен на рис. 11.4.
Если теперь осуществить вызов Release (P), то указатель заполнения кучи HeapPtr переустановится в «позицию», которая была запомнена ранее в указателе P. Куча примет вид, показанный на рис. 11.5.
Рис. 11.5
- 204 -
Действие Release (P) проявилось в том, что куча вернулась в предыдущее состояние, «забыв» обо всех динамических переменных, созданных после выполнения процедуры Mark(P). Теперь уже не надо освобождать ссылки P3 и P4. Их нет, как будто они и не размещались.
Из механизма работы процедуры Release следует, что всю кучу можно освободить одним оператором Release ( HeapOrg ), который приводит ее к исходному пустому состоянию.
Очевидно, что пара процедур Mark/ Release — это очень мощное средство управления кучей. Они позволяют эффективно освобождать память с минимальными усилиями. Однако за это приходится платить гибкостью использования пространства кучи. При их применении освобождение памяти должно производится в порядке, обратном размещению динамических переменных. Так, например, нельзя удалить переменную P2^, не удалив при этом переменные P3^ и P4^. Для более гибкого использования кучи необходимо применять процедуры Dispose и FreeMem.
Вместе с тем не рекомендуется перемежать вызовы процедур Release с вызовами процедур Dispose и FreeMem. Две последние действуют избирательно и могут освобождать блоки памяти в используемой части кучи. Так, если после строки New(P4) в рассмотренном примере поставить вызов Dispose (P1), то перед выполнением Release (P) на месте динамической переменной P1^ будет пусто, и это пустое место могло бы быть потом использовано для размещения других данных. Координаты этого пустого (свободного) блока хранятся в специальном списке свободных блоков. Процедура Release (P) среди всего прочего стирает список свободных блоков, навсегда блокируя тем самым доступ к свободным блокам, находящимся ниже значения указателя P. Об этом надо помнить всегда, иначе можно легко и незаметно заблокировать всю кучу и потерять массу времени на нетривиальное решение вечного вопроса «почему программа не пошла?»
11.5.4. Функции MaxAvail и MemAvail
Обе эти функции анализируют количество свободной памяти, как еще не использованной, так и получившейся в результате освобождения динамических переменных процедурами Dispose и FreeMem. Возвращаемые значения этих функций имеют тип LongInt.
Функция MaxAvail возвращает длину в байтах самого длинного свободного блока. Она находится как максимальное значение из размера свободной части кучи и размеров освобожденных к данному
- 205 -
моменту блоков. Выдаваемое MaxAvail значение имеет смысл размера наибольшей сплошной структуры данных (как массив, запись, объект), которая могла бы уместиться в куче. Правда, такая сплошная структура данных сама ограничена размером в 65520 байт.
Полный объем свободного пространства (памяти) в куче можно опросить функцией MemAvail. Она вернет сумму длин всех свободных блоков в куче: и освобожденных, и еще ни разу не использованных.