Эти две функции полезны при анализе ресурсов памяти перед размещением динамических переменных, особенно процедурой GetMem. На рис. 11.6 показан пример их использования.
| PROGRAM TestHeap; {ПРОГРАММА, АНАЛИЗИРУЮЩАЯ МЕСТО В КУЧЕ}
| TYPE
| Dim = Array [1..5000] of LongInt; { базовый тип }
| VAR
| P : ^Dim; { ссылка на базовый тип — массив }
| Psize : LongInt; { переменная для анализа размера }
| CONST
| SL = SizeOf(LongInt); { размер элемента массива }
| BEGIN
| WriteLn( 'В куче свободно ', MemAvail, ' байт' );
| {Psize округляется до целого числа значений LongInt:}
| Psize := SL*(MaxAvail div SL);
| if SizeOf(Dim) > Psize
| then begin { мало места в куче }
| WriteLn('Массив P^ не может быть размещен целиком');
| GetMem( P. Psize ); { отводим Psize байт }
| WriteLn( ' Размещено ', Psize div SL, ' элементов' )
| end
| else begin { достаточно места }
| New( P ); { размещаем массив }
| Psize := SizeOf(Dim); { объем массива P^ }
| WriteLn( 'Динамический массив P размещен' )
| end;
| { ... работа с динамическим массивом ... }
| FreeMem(P, Psize);{универсальное освобождение массива}
| END.
Рис. 11.6
11.5.5. Более детальный анализ состояния кучи
Этот подраздел посвящен детальному разбору механизма ведения учета свободных блоков в куче, и может быть опущен при ознакомительном чтении без потерь для целостности изложения.
- 206 -
При использовании процедур Dispose и FreeMem куча становится фрагментированной, т. е. в ней появляются свободные блоки. Эти блоки могут возникать в любом порядке и со временем куча может превратиться в подобие решета. Но работоспособность кучи не исчезнет.
Адреса и размеры свободных блоков сохраняются в так называемом списке свободных блоков, который имеет стековую структуру и растет сверху вниз от верхней границы области кучи навстречу указателю заполнения кучи HeapPtr. При попытке размещения динамической переменной осуществляется просмотр списка свободных блоков. Если найден свободный блок подходящего размера, то именно он и используется.
Указатель на список свободных блоков хранится в предопределенной системной переменной FreePtr типа Pointer. Эта переменная фактически указывает на массив записей типа FreeList, т.е. соответствует типу FreeListP:
TYPE
FreeRec = RECORD { Указатели на }
OrgPtr, EndPtr : Pointer; { начало и конец }
END; { блока в куче }
FreeList = Array [0..8190] of FreeRec;
FreeListP = ^FreeList; { ссылка на массив записей }
Поля OrgPtr и EndPtr каждой записи определяют начало и конец каждого свободного блока (EndPtr, точнее говоря, указывает на первый байт после блока) и являются нормализованными указателями.
Фактическое значение самой FreePtr не является постоянным, а как бы перемещается в диапазоне от верхней границы кучи до максимальной длины списка свободных блоков.
Число освобожденных пустых блоков (элементов в массиве FreeList) на текущий момент можно вычислить по формуле
FreeCount := (8192 - Ofs(FreePtr^) div 8) mod 8192;
Максимальное число свободных блоков, которые могут существовать одновременно, равно емкости массива FreeList (8191 блок). Число это достаточно велико, чтобы достичь его на практике. Если же это удастся, то возникнет фатальная ошибка и останов программы.
Ошибка может возникнуть также, когда куча заполнена и список свободных блоков почти смыкается с верхней границей заполнения кучи. При этом попытка освобождения блока, не лежащего на вершине кучи, будет приводить к расширению списка и возникновению ошибочной ситуации. Для решения этой проблемы монитор кучи использует системную переменную FreeMin типа Word, которую можно применять для управления минимальным размером
- 207 -
участка памяти между HeapPtr и списком свободных блоков (FreePtr). Для этого необходимо в FreeMin записать гарантированный размер этого участка в байтах. Чтобы при этом не происходило потерь памяти, значение размера должно быть вычислено как
ЧИСЛО_ЭЛЕМЕНТОВ_СПИСКА_В_ЗАЗОРЕ*8,
где 8 — размер записи FreeRec. Когда значение FreeMin не равно нулю, вызовы процедур New и GetMem будут неэффективными, если они пытаются сделать расстояние между HeapPtr и FreePtr меньше FreeMin. Кроме того, MaxAvail и MemAvail будут вычитать FreeMin из возвращаемых значений.