Руководство по языку B.Pascal 7

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


Динамические переменные, сохраняемые в динамически распреде- ляемой области, освобождаются одним из двух следующих способов:

1. С помощью процедур Dispose или FrееМем.

2. С помощью процедур Маrk и Rеlеаsе.

Простейшей схемой использования процедур Маrk и Rеlеаsе, например, является выполнение следующих операторов:

New(Ptr1); New(Ptr2); Mark(P); New(Ptr3); New(Ptr4); New(Ptr5);

Схема динамически распределяемой области при этом будет выг- лядеть, как показано на Рис. 21.2.

HeapEnd -->--------------------------- Верхняя граница ¦ ¦ памяти ¦ ¦ HeapPtr -->+--------------------------+ ¦ содержимое Ptr5^ ¦ Ptr5 -->+--------------------------+ ¦ содержимое Ptr4^ ¦ Ptr4 -->+--------------------------+ ¦ содержимое Ptr3^ ¦ Ptr3 -->+--------------------------+ ¦ содержимое Ptr2^ ¦ Ptr2 -->+--------------------------+ ¦ содержимое Ptr1^ ¦ Ptr1 -->L--------------------------- Нижняя граница памяти

Рис. 21.2 Метод освобождения областей динамически распреде- ляемой области помощью процедур Маrk и Rеlеаsе.

Оператор Маrk(P) отмечает состояние динамически распределяе- мой области непосредственно перед выделением памяти для перемен- ной Ptr3 (путем сохранения текущего значения переменной НеаpPtr в P). Если выполняется оператор Rеleаsе(P), то схема динамически распределяемой области становится такой, как показано на Рис. 21.3. При этом, поскольку производится обращение к процедуре Маrk, освобождается память, выделенная под все указатели.

Примечание: Выполнение процедуры Rеleаsе(НеаpОrg) пол- ностью освобождает динамически распределяемую область памя- ти, поскольку переменная НеаpOrg указывает на нижнюю грани- цу динамически распределяемой области.

HeapEnd -->--------------------------- Верхняя граница ¦ ¦ памяти ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ HeapPtr -->+--------------------------+ ¦ содержимое Ptr2^ ¦ Ptr2 -->+--------------------------+ ¦ содержимое Ptr1^ ¦ Ptr1 -->L--------------------------- Нижняя граница памяти

Рис. 21.3 Схема динамически распределяемой области при вы- полнении процедуры Rеleаsе(P).


Применение процедур Маrk и Rеlеаsе для освобождения памяти, выделенной для динамических переменных, на которые ссылаются ука- затели, выполняемое в порядке, в точности обратном порядку выде- ления памяти, весьма эффективно. Однако в большинстве программ имеется тенденция в более случайному выделению и освобождению па- мяти, отведенной для динамических переменных, на которые ссылают- ся указатели, что влечет за собой необходимость использования бо- лее тонких методов управления памятью, которые реализованы с по- мощью процедур Dispose и FrееMem. Эти процедуры позволяют в любой момент освободить память, выделенную для любой динамической пере- менной, на которую ссылается указатель.

Когда с помощью процедур Dispose и FrееМем освобождается па- мять, отведенная для динамической переменной, не являющаяся "са- мой верхней" переменной в динамически распределяемой области, то динамически распределяемая область становится фрагментированной. Предположим, что выполнялась та же последовательности операторов, что и в предыдущем примере. Тогда после выполнения процедуры Dispose(Ptr3) в центре динамически распределяемой области памяти образуется незанятое пространство ("дыра"). Это показано на Рис. 21.4.

HeapEnd -->--------------------------- Верхняя граница ¦ ¦ памяти ¦ ¦ HeapPtr -->+--------------------------+ ¦ содержимое Ptr5^ ¦ Ptr5 -->+--------------------------+ ¦ содержимое Ptr4^ ¦ Ptr4 -->+--------------------------+ ¦--------------------------¦ ¦--------------------------¦ +--------------------------+ ¦ содержимое Ptr2^ ¦ Ptr2 -->+--------------------------+ ¦ содержимое Ptr1^ ¦ Ptr1 -->L--------------------------- Нижняя граница памяти

Рис. 21.4 Создание незанятой области ("дыры") в динамически распределяемой области памяти.

Если в данный момент выполняется процедура New(Ptr3), то это опять приведет к выделению той же области памяти. С другой сторо- ны, выполнение процедуры Dispose(Ptr4) увеличит размер свободного блока, так как Ptr3 и Ptr4 были соседними блоками (см. Рис. 21.5).



HeapEnd -->--------------------------- Верхняя граница ¦ ¦ памяти ¦ ¦ HeapPtr -->+--------------------------+ ¦ содержимое Ptr5^ ¦ Ptr5 -->+--------------------------+ ¦--------------------------¦ ¦--------------------------¦ ¦--------------------------¦ ¦--------------------------¦ +--------------------------+ ¦ содержимое Ptr2^ ¦ Ptr2 -->+--------------------------+ ¦ содержимое Ptr1^ ¦ Ptr1 -->L--------------------------- Нижняя граница памяти

Рис. 21. 5 Увеличение размера незанятого блока памяти.

В конечном итоге выполнение процедуры Dispose(Ptr5) приведет сначала к созданию незанятого блока большего размера, а затем НеаpPtr переместится в более младшие адреса памяти. Поскольку последним допустимым указателем теперь будет Ptr2 (см. Рис. 21 6), то это приведет к действительному освобождению незанятого блока.

HeapEnd -->--------------------------- Верхняя граница ¦ ¦ памяти ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ HeapPtr -->+--------------------------+ ¦ содержимое Ptr2^ ¦ Ptr2 -->+--------------------------+ ¦ содержимое Ptr1^ ¦ Ptr1 -->L--------------------------- Нижняя граница памяти

Рис. 21.7 Освобождение незанятого блока памяти.

Как показано на Рис. 21.7, динамически распределяемая об- ласть памяти теперь находится в том же самом состоянии, в каком она находилась бы после выполнения процедуры Rеlеаsе(P). Однако создаваемые и освобождаемые при таком процессе незанятые блоки отслеживаются для их возможного повторного использования.


Содержание раздела