Анимация
JavaScript
|
Главная Библионтека Класс VoidPtrPool идентичен тому, который использовался в алгоритме Бейкера, с добавлением связанного списка активных VoidPtr. Итератор ведущих указателей Итератор ведущих указателей устроен элементарно. Он просто перебирает элементы списка от начала к концу - иначе говоря, от нижних адресов памяти к верхним. class VoidPtrPoollterator : public VoidPtrlterator { private: VoidPtr* next; public: VoidPtrIterator(VoidPtr* first) : next(first) {} virtual bool More() { return next != NULL; } virtual VoidPtr* Next() VoidPtr* vp = next; next = next->next; return vp; VoidPtrIterator* VoidPtrPoo1::iterator() return new VoidPtrPoolIterator(&head.next); Алгоритм уплотнения Алгоритм уплотнения выглядит так просто, что его можно было бы и не приводить. class Space { private: unsigned long next byte; unsigned char bytes[SPACESIZE]; public: Space() : next byte(0) {} void* Al1ocate(size t size); void Compact(); void* Space::Al1ocate(size t size) Выровнять на границу слова size = ROUNDUP(size); if (next byte + size > SPACESIZE) Compact(); if (next byte + size > SPACESIZE) Исключение - нехватка памяти void* space = &bytes[next byte]; next byte += size; return space; void Space::Compact() next byte = 0; VoidPtrIterator* iterator = VoidPtrPoo1::iterator(); while (iterator->More()) VoidPtr* vp = iterator->Next(); void* space = Al1ocate(vp->size); if (space < vp->address) Проверить, что объект поместится delete iterator; Оптимизация Существует много возможностей повысить эффективность этой схемы для рядовых классов. Один из простейших видов оптимизации - хранение нижнего уровня, который определяет самый нижний удаленный объект. Нижний уровень представляет собой самый нижний VoidPtr из активного списка, ниже которого удаленных объектов нет. Он хранится в виде переменной класса VoidPtr* вместе с началом и концом списка. Деструктор VoidPtr проверяет, находится ли адрес указываемого объекта ниже текущего нижнего уровня; если да, он заменяет нижний уровень новым значением. Уплотнение начинается с нижнего уровня, поскольку ниже него перемещать ничего не требуется. Иначе говоря, мы начинаем не с начала списка, а где-то с середины - с нижнего уровня. Этот прием особенно полезен, если учитывать специфику уплотнения на месте. Было замечено, что чем старше становится объект, тем меньше вероятность того, что он будет удален в ближайшее время. Старые объекты в этой схеме группируются в нижней части пула. Возникает большой блок объектов, которые практически не перемещаются, поскольку нижний уровень всегда находится над ними. Другой прием оптимизации - объединять несколько смежных объектов в одной операции перемещения. Экономия не так уж велика, но несколько тактов все же удается сэкономить. Последовательное уплотнение на месте Алгоритм уплотнения на месте нетрудно приспособить для условий последовательного уплотнения в фоновом режиме. Все просто: мы сохраняем VoidPtrIterator в виде переменной класса Space и используем его, чтобы смещать вниз один объект при каждом вызове функции Copy1(). Реализация проста и прямолинейна, необходимо лишь соблюдать осторожность с удалением во время уплотнения. Помните, что в процессе перебора списка VoidPtr удаляется один из его элементов. Это простой частный случай проблемы надежности итераторов, которую мы обсуждали в главе 8. Все, что говорилось об алгоритме Бейкера в контексте внешних объектов, в равной степени относится и к уплотнению на месте. Адреса, на которые ссылаются VoidPtr, следует проверять по интервалам адресов в пространстве уплотнения, и объекты могут свободно перемещаться из пространства памяти и в него. Глава получилась очень длинной, а подобная схема уплотнения редко применяется на практике. Подробности оставляю читателю в качестве самостоятельных упражнений. Перспективы Программистам на С++ обычно не так уж часто приходится беспокоиться об уплотнении, поэтому все сказанное стоит воспринимать со здоровым скепсисом. Если вам удастся организовать нормальный подсчет ссылок, уплотнение может оказаться приятным дополнением, но в целом эта глава готовит почву для приемов сборки мусора, рассмотренных в следующей главе. Кроме того, она помогает очертить внешние границы языка, поскольку С++ по своей сути не рассчитан на свободное перемещение объектов в памяти. В следующей главе мы вернемся к стратегии «дескрипторов повсюду». Конечно, организовать универсально уплотнение памятью для производных коммерческих классов невозможно. Но по крайней мере вы увидите, как далеко можно зайти в С++, прежде чем поднять руки, сведенные судорогой от долгого сидения за клавиатурой. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 [ 75 ] 76 77 78 79 80 81 82 |