Основными механизмом работы с памятью в .NET являются неявное управления памятью, стековый механизм выделения памяти и сборка мусора, использующая поколения и механизм "разметки-и-уплотнения" (mark-and-compact). Сразу оговоримся, что платформа .NET содержит также средства, позволяющие обойти эти ограничения, например, C# содержит специальный оператор fixed и некоторые средства управления механизмом сборки мусора, но в данной лекции мы их проигнорируем.
Для начального выделения памяти простой стековый механизм: имеется один указатель на следующее свободное место в куче, который после помещения в кучу очередного объекта увеличивается на его размер. Понятно, что в какой-то момент указатель кучи может выйти за пределы доступного адресного пространства - в этот момент начинает работу алгоритм сборки мусора. В целях оптимизации процесса сборка мусора чаще всего ограничивается обходом нулевого поколения - чаще всего этого оказывается достаточно.
Для сборки мусора производится маркировка активных элементов; она начинается с так называемых корневых объектов, список которых хранится в JIT-компиляторе .NET и предоставляется сборщику мусора. По окончании маркировки все активные элементы сдвигаются к началу кучи путем простого копирования памяти. Так как эта операция компрометирует все указатели, сборщик мусора также исправляет все ссылки, используемые программой.
Реально алгоритм сборки мусора, используемый в .NET, существенно сложнее, так как включает в себя такие оптимизации как слабые ссылки, отдельную кучу для крупных объектов, сборку мусора в многопоточных приложениях и т.д. Подробное описание алгоритма сборки мусора .NET можно найти в статье Дж. Рихтера "Automatic Memory Management in the Microsoft .NET Framework", MSDN Magazine, Nov/Dec 2000.