Статьи Королевства Дельфи


Диспетчер кучи для объектов одного размера - часть 2


with psnFixSzMemMgr.Create(16) do begin // куча создана Ptr:= FixNew; // Выделяем блок в 16 байт. ... // Делаем всё, что нам надо. FixDsp(Ptr); // Блок, указуемый Ptr, возвращён в кучу. Вообще-то // этот шаг перед самым разрушением кучи // необязателен, это стоит делать, только когда // есть надежда на повторное использование блока. Destroy; // Куча разрушена. end;

Статистика: с 8-12-байтными блоками втрое обгоняет стандартный, с возрастанием блока до 48 байт практически сравнивается с ним. Экономия памяти при хорошем наполнении составляет почти 4 байта на блок.

У него есть недостаток, вытекающий из назначения. Рассмотрим в общих чертах его работу. Он выделяет большой массив памяти, нарезает его блоками и скармливает нам, пока он не кончится, выделяет следующий и т.д. Но определением того факта, что в массиве уже нет ни одного используемого блока, и освобождением массива этот диспетчер кучи не занимается по соображениям скорости. Иными словами, грубо говоря, если Вы выделили 1024 блока памяти по 16 байт, а затем все их освободили, пропадёт 16 кБ памяти до освобождения либо заполнения заново этой кучи.

Но динамические структуры чаще всего применяются однократно или не уменьшаются так резко в ходе работы. Не проблема и реализовать несколько замедленный вариант диспетчера кучи с автоосвобождением. Более того, если идёт интенсивная работа с мелкими блоками, вероятность того, что полностью освободится целый массив, невелика. А если не смущает некоторая замедленность работы, то для блоков произвольного размера можно воспользоваться API (LocalAlloc, HeapCreate и пр.), позволяющими реализовать отдельную кучу.

У отдельной кучи вообще есть большой плюс - динамическую структуру данных, размещённую в общей куче, долго и в некоторых случаях сложно уничтожать (утечки памяти). А тут это делается просто деструктором (это НЕ касается структур, содержащих длинные строки, динамические массивы и интерфейсы, а также объектов с таковыми - тут, увы, можно применять разве что SetMemoryManager и TObject.NewInstance...).

Я обнаружил, что запрошенная посредством VirtualAlloc (как делает "настоящий" диспетчер памяти) память в окне CPU Delphi 5 отображается как мусор: изменить память там можно, а посмотреть - нет (с всплывающей подсказкой в редакторе кода всё нормально). Так что диспетчер реализован как надстройка над стандартным, что, конечно, несколько снижает скорость, но зато позволяет уменьшить потери памяти на фрагментацию для небольших объёмов (VirtualAlloc не размещает память меньше чем по странице - 4 кБ на процессорах линии IA32).

Исходный код модуля:




Начало  Назад  Вперед