Модуль «System». Константы. Переменные. Стандартные процедуры и функции, страница 6

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

Список свободных блоков.

        Адреса и размеры свободных блоков,  созданных при  операциях Dispose  и FrееМем,  хранятся в списке свободных блоков,  который увеличивается вниз, начиная со старших адресов памяти, в сегменте динамически  распределяемой области.  Каждый раз перед выделением памяти для динамической переменной,  перед тем,  как  динамически распределяемая  область будет расширена,  проверяется список свободных блоков.  Если имеется блок  соответствующего  размера  (то есть размер которого больше или равен требуемому размеру),  то он используется.
        Процедура Rеlеаsе всегда очищает  список  свободных  блоков. Таким образом,  программа динамического распределения памяти "забывает" о незанятых блоках,  которые могут существовать ниже указателя динамически распределяемой области.  Если вы чередуете обращения к процедурам Маrk и Rеlеаsе с  обращениями  к  процедурам Dispose и FrееМем, то нужно обеспечить отсутствие таких свободных блоков.
        Переменная FreeList модуля System указывает на  первый  свободный  блок  динамически  распределяемой области памяти.  Данный блок содержит указатель на следующий свободный блок и  т.д.  Последний  свободный  блок содержит указатель на вершину динамически распределяемой области (то есть адрес,  заданный  HeapPtr).  Если свободных блоков в списке свободных блоков нет, то FreeList будет равно HeapPtr.
 
Формат первых  8  байт  свободного  блока   задается   типом
TFreeRec:
        type
            PFreeRec = ^TFreeRec;
            TFreeRec = record
                      Next: PFreeRec;
                        Size: Pointer;
                        end;
        Поле Next указывает на следующий свободный блок,  или на  ту же ячейку,  что и HeapPtr, если блок является последним свободным блоком.  В поле Size записан размер свободного блока.  Значение в поле  Size  представляет собой не обычное 32-битовое значение,  а "нормализованное" значение-указатель с числом свободных  параграфов  (16-байтовых  блоков)  в старшем слове и счетчиком свободных байт (от 0 до 15) в младшем слове.  Следующая  функция  BlockSize преобразует значение поля Size в обычное значение типа Longint:
 
            function BlockSize(Size: Pointer): Longint;
            type
                PtrRec = record Lo, Hi: Word end;
            begin
                 BlockSize := Longint(PtrRec(Size)).Hi)*16+PtrRec(Size).Lo
            end;
        
Чтобы обеспечить, что в начале свободного блока всегда имеется место для TFreePtr,  подсистема управления динамически распределяемой областью памяти округляет размер каждого блока, выделенного подпрограммами New и GetMem  до  8-байтовой  границы.  Таким образом,  8  байт выделяется для блоков размером 1..8,  16 байт - для блоков размером 9..16 и т.д. Сначала это кажется непроизводительной тратой памяти.  Это в самом деле так, если бы каждый блок был размером 1 байт. Но обычно блоки имеют больший размер, поэтому относительный размер неиспользуемого пространства меньше.
        8-байтовый коэффициент  раздробленности  обеспечивает,  что при большом числе случайного выделения и освобождения блоков  относительно небольшого размера (что типично для записей переменной длины в программах обработки текста) не приведет к сильной  фрагментации  динамически распределяемой области.  В качестве примера предположим,  что занимается и  освобождается  блок  размером  50 байт.  После  его  освобождения  запись о нем включается в список свободных блоков.  Этот блок округляется до 56 (7*8) байт. Если в дальнейшем потребуется блок размером от 49 до 56 байт,  то данный блок будет полностью повторно использован, а не останется от 1 до 7 байт памяти (использование который маловероятно), которые будут только фрагментировать динамически распределяемую область.