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. Об этом надо помнить всегда, иначе можно легко и незаметно заблокировать всю кучу и потерять массу времени на нетривиальное решение вечного вопроса «почему программа не пошла?»