Иногда необходимо знать величину еще ни разу не использованного пространства кучи (между значениями указателей FreePtr сверху и HeapPtr снизу). Функция HeapAvail, анализирующая размер именно этого пространства (без учета освобожденных блоков в куче), приводится на рис. 11.7.

| { $М 1024, 4000, 4000} (*заданные параметры кучи для теста*)

| FUNCTION HeapAvail : LongInt;

| VAR

| LongSeg, LongFreePtr, LongHeapPtr : LongInt;

| BEGIN

| LongSeg := Seg(FreePtr^)+$1000*Byte(Ofs(FreePtr^)=0);

| LongFreePtr :=(LongSeg * 16 ) + Ofs(FreePtr^);

| LongSeg := Seg( HeapPtr^);

| LongHeapPtr := ( LongSeg*16 ) + Ofs( HeapPtr^);

| HeapAvail := LongFreePtr – LongHeapPtr

| END;

| procedure WriteAvail; { вспомогательная процедура }

| begin

| WriteLn( ' MemAvail=', MemAvail :6,

| ' MaxAvail=', MaxAvail :6,

| ' HeapAvail=', HeapAvail :6 )

| end; {WriteAvail}

| VAR { ПРИМЕР АНАЛИЗА ПАМЯТИ КУЧИ }

| P1, P2 : Pointer;

| BEGIN

| WriteLn( 'Начало работы:' ); WriteAvail;

| GetMem ( P1, 1000 ); { отводится 1000 байт в куче }

| GetMem ( Р2, 10 ); { отводится 10 байт в куче }

| WriteLn('Размещены в куче 2 переменные(1000 и 10 б)');

| WriteAvail;

| FreeMem( P1, 1000 ); { освобождается первый блок }

- 208 -

{Сейчас в куче появилась дыра, а в списке свободных блоков появились ее координаты, уменьшив кучу на 8 байт.}

| WrfteLn( 'Освобождена ссылка на 1000 байт:' );

| WriteAvail;

| FreeMem( P2, 10 ); { освобожден второй блок }

| { Теперь вся куча пуста, и нет нужды в списке блоков. }

| WriteLn( 'Освобождена ссылка на 10 байт:' );

| WriteAvail;

| ReadLn { пауза до нажатия клавиши ввода }

| END.

Рис. 11.7 (окончание)

Заметьте, что все локальные параметры в функции HeapAvail имеют тип LongInt, чтобы хранить десятичные значения ненормализованных (абсолютных) адресов. Чтобы избежать потери порядка из-за превышения диапазона типа Word, значения функций Seg(...) перед умножением переписываются в переменную более «длинного» типа LongInt.

Следующий вопрос, связанный со списком свободных блоков, — это потенциальная проблема дробности. Она связана с тем, что дробность в мониторе кучи равна одному байту. Это означает, что при размещении одного байта переменная будет занимать один байт. При размещении и удалении в произвольном порядке большого числа переменных или блоков различной длины могут появляться свободные блоки небольшого размера, причем число таких блоков может быть большим. Так, например, при освобождении блока размером 20 байт и размещении вслед за этим блока размером 19 байт, появится свободный блок длиной 1 байт, который может находиться в списке свободных блоков довольно долго. Для решения этой проблемы справочное руководство по Турбо Паскалю советует воспользоваться следующим приемом. Каждый раз выделять и впоследствии освобождать блок с размером, кратным какому-либо целому числу байтов. Для этого необходимо переопределить процедуры GetMem и FreeMem. Например, если мы хотим выделять память блоками с размером, кратным 16 байт, то переопределения GetMem и FreeMem будут выглядеть следующим образом:

PROCEDURE GetMem( VAR P : Pointer; Size : Word );

BEGIN

System.GetMem(P, (Size+15) and $FFF0);

END; { GetMem }

- 209 -

PROCEDURE FreeMem( VAR P : Pointer; Size : Word );

BEGIN

System.FreeMem( P, (Size+15) and $FFF0);

END; { FreeMem }

В этом случае минимальный размер свободного блока будет не меньше 16 байт. Однако это будет справедливо до тех пор, пока не будут применены процедуры New и Dispose. Процедура New, например, может для переменной, имеющей размер, не кратный 16 байт, использовать такой свободный блок, что останется пустой излишек с размером менее 16 байт. И уж он-то будет находиться в памяти очень долго. Ситуация осложняется еще тем, что мы теперь уже не можем так просто управлять размером выделяемого блока. Возможным средством улучшить ситуацию является приведение размера динамической переменой к величине, кратной 16 байт. Так, например, если необходимо размещать в куче переменные типа:

TYPE

Dot = RECORD x,y : Real END;

Перейти на страницу:

Поиск

Похожие книги