Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
организация памяти.doc
Скачиваний:
13
Добавлен:
09.02.2015
Размер:
186.37 Кб
Скачать

ПроцедурыGetMemиFreeMem

Процедура GetMem(Var P: Pointer; Razmer:Word) выделяет в куче непрерывный блок ячеек с требуемым размером в Razmer байт и адрес первой ячейки этого блока присваивает указателю Р.

Процедура FreeMem освобождает в куче непрерывный блок ячеек с размером в Razmer байт, начиная с адреса, записанного в указателе Р (Get и Free — соответственно выделять и освобождать, Mem — от Memory — память).

Процедура GetMem аналогична процедуре New, а процедура FreeMem — процедуре Dispose. Если в процедурах New и Dispose размер блока под объект динамической переменной определяется автоматически на основе типа выбранной динамической переменной (или, как часто говорят, — типа данных, на который ссылается выбранный указатель), то в процедурах GetMem и FreeMem размер такого блока должен контролироваться программистом.

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

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

Сходство упомянутых пар процедур позволяет использовать их одновременно в одной и той же программе. Более того, в паре процедур New-Dispose вместо процедуры New можно применять процедуру GetMem, вместо Dispose — FreeMem и, наоборот, — в паре GetMem-FreeMem вместо GetMem — New, а вместо FreeMem — Dispose. К сожалению, при работе пары процедур GetMem и FreeMem также возможна фрагментация кучи.

Управление блоками динамической памяти

Исключить фрагментацию кучи позволяет применение процедур Mark (метить) и Release (освобождать).

Процедура Mark(Var P: Pointer) запоминает в указателе Р нижнюю текущую границу свободной памяти кучи — адрес первой свободной ячейки, расположенной сразу же за конечной ячейкой объекта самой последней динамической переменной. Таким образом, в Р запоминается адрес первой ячейки неиспользуемой части кучи. В этом случае динамическая переменная-указатель фактически дублирует функции типизированной константы-указателя HeapPtr и с успехом может быть заменена ею.

Процедура Release(Var P:Pointer) возвращает кучу в состояние, которое было зафиксировано ранее в Р вызовом процедуры Mark. Если после вызова процедуры Mark(P) вы порождали (при помощи New или GetMem) целый ряд новых динамических переменных (любых типов), то вызов процедуры Release (P) после таких порождений передвигает нижнюю границу свободного пространства кучи HeapPtr с позиции (с адреса), которую она занимала после порождения последней динамической переменной, в позицию (в адрес), которая ранее была зафиксирована в указателе Р. При этом все переменные, порожденные после вызова процедуры Mark(P), уничтожаются и все смежные блоки ячеек уничтоженных переменных пригодны для размещения в них любых других динамических переменных без эффекта фрагментации.

Если перед порождением первой динамической переменной в типизированную константу-указатель HeapOrg записать исходное состояние кучи (при помощи Mark(HeapOrg)), то вызовом процедуры Release (HeapOrg) можно полностью освободить кучу от всех динамических переменных, которые до такого вызова порождались, например, вызовом процедуры New.

Приведем поясняющий фрагмент программы:

Var

Р:Pointer;

Pl,P2,P3,P4:^Real;

BEGIN

New(Pl);

New(P2);

Mark(P);

New(P3);

New(P4);

Mark(HeapPtr) ;

Release(P)

Mark(HeaPtr);

После вызова двух последних процедур приведенного фрагмента программы в указателях Р и PheaPtr будет записан один и тот же адрес — указатель первой ячейки кучи, расположенной за последней ячейкой объекта самой «верхней» динамической переменной Р2. Переменные РЗ и Р4, порожденные после Р2, будут уничтожены и адрес, записанный в Р, окажется адресом первой ячейки неиспользованной памяти кучи: новой нижней границы свободной области кучи.

При таком способе управления кучей нельзя удалить, например, переменную Р2, не удалив при этом переменные РЗ и Р4. Это ограничение является определенным недостатком процедур Mark и Release. Для более гибкого использования кучи необходимо применять процедуру Dispose или FreeMem.

В одной и той же программе не рекомендуется применение «блочных» процедур Mark и Release совместно с процедурами Dispose и FreeMem. Это обусловлено следующим обстоятельством. При использовании процедур Dispose и FreeMem параллельно с освобождением ими блоков памяти объектов конкретных динамических переменных адреса (координаты) освобожденных блоков заносятся в специальный список адресов и объемов свободных блоков, который применяется всякий раз для последующего размещения в куче новых динамических переменных. Процедура же Release(P) не только перемещает указатель на новую нижнюю границу свободной части кучи, но и стирает весь список свободных блоков. Таким образом, если освобожденный процедурой Dispose или FreeMem блок памяти оказался ниже указателя Р, то после применения процедуры Release (P) до конца работы программы в этом блоке больше не размещаются новые динамические переменные с использованием процедур New или GetMem. Это необходимо всегда помнить, дабы постепенно не заблокировать всю кучу от размещения там каких-либо переменных.

В заключение отметим, что хорошим стилем программирования является уничтожение динамических переменных после их обработки (если это даже не требуется для размещения новых динамических переменных) или в конце программы. Это означает, что для каждой процедуры New должна иметься парная ей процедура Dispose, а для каждой процедуры GetMem — процедура FrееМеm. Если применяется блочная работа с динамической памятью при помощи подпрограмм Маrk Re lease, то перед порождением первой динамической переменной необходимо вызвать процедуру Mark(P), а в конце программы — процедуру Release(P) для полного освобождения кучи от динамических переменных вашей программы. Нарушение этих рекомендаций не приведет к сбоям в работе программы, а будет лишь свидетельствовать о плохом стиле программирования автора программы.

9