Главная страница
Библиотека (скачать книги)
Скачать софт
Введение в программирование
Стандарты для C++
Уроки по C#
Уроки по Python
HTML
Веб-дизайн
Ассемблер в среде Windows
ActiveX
Javascript
Общее о Линукс
Линукс - подробно
Линукс - новое
Delphi
Паскаль для начинающих
Турбопаскаль
Новости
Партнеры
Наши предложения
Архив новостей





Сборка мусора.

Предисловие

Автоматическая сборка мусора (выделенной и более не используемой памяти) предназначена для облегчения жизни программисту, позволяя не заботиться о возможных утечках памяти (например, удалить выделенный в куче объект). С одной стороны - безусловно благие намерения, с другой - получение "бесплатных тормозов" при работе оного сборщика мусора (garbage collector). Хотя, наверное, приятно не обращать внимание на такие досадные мелочи, как удаление выделенной памяти :) .

Выделение памяти

По идее, память из кучи в среде .NET Runtime выделяется достаточно быстро. Системе всего лишь необходимо удостовериться, что в управляемой куче достаточно памяти для запрашиваемого объекта, вернуть указатель на эту память и перевести указатель на следующий после последнего объекта адрес. Соответственно, простота выделения памяти сопровождается некоторым усложнением очистки.

Для повышения быстродействия большие объекты (>20 КБайт) выделяются из отдельной кучи.

Пометка и сжатие

Работа сборщика мусора основана на алгоритме "пометки и сжатия". Сборщик мусора начинает свою работу с просмотра корневых объектов (включая глобальные, статические, локальные объекты и регистры процессора) и нахождения всех объектов, ссылки на которые присутствуют в корневых объектах. Объекты, используемые на момент сборки мусора, помечаются, а все остальные объекты в системе считаются ненужными.

Процесс сборки мусора завершается копированием всех используемых объектов в начало управляемой кучи и соответствующей модификацией указателей на объекты. Затем указатель устанавливается на следующий доступный блок после последнего используемого объекта.

Поскольку сборщик мусора перемещает объекты и изменяет ссылки на них, в это время в системе не может выполняться никаких других операций. То есть во время работы сборщика вся полезная работа прекращается.

Поколения

Перебор всех объектов, на которые существуют ссылки, обходится дорого. Более того, основная часть работы будет потрачена впустую - чем старше объект, тем больше вероятность того, что он продолжает использоваться. Соответственно, ссылки на новые объекты с большей вероятностью перестают использоваться.

Для воплощения этого принципа в сборщике .NET использована концепция поколений (generations). Объекты в куче делятся на три поколения.

Поколение 0 составляют объекты, никогда не проходившие сборку мусора. Объекты поколения 1 пережили одну сборку мусора, а объекты поколения 2 прошли сборку мусора несколько раз.

Когда среде потребуется выполнить сборку мусора, она сначала выполняет сборку мусора среди объектов поколения 0. Именно к этому поколению относится большая часть неиспользуемых объектов, что обеспечивает освобождение максимальной памяти при минимальных затратах. Если сборка мусора в этом поколении не освобождает достаточно памяти, система переходит к поколению 1 и, если потребуется, - к поколению 2.

Пусть в табличке изображены находящиеся в куче объекты перед сборкой мусора. Числовой суффикс обозначает поколение объекта; первоначально все объекты относятся к поколению 0. Активными являются только те объекты, которые находятся в таблице. Допустим, необходимо выделить память для дополнительных объектов.

A0 B0 C0 D0 E0
Начальное состояние памяти перед сборкой мусора

На момент первой сборки мусора продолжают использоваться только объекты B и D. В результате сборки куча переходит в состояние, отображенное в таблице.

B1 D1
Состояние памяти после первой сборки мусора

Поскольку объекты B и D пережили первую сборку, они переходят в поколение 1. Затем в системе создаются новые объекты (см. таблицу).

B1 D1 F0 G0 H0 J0
Создание новых объектов

Через некоторое время, на момент очередной сборки мусора, продолжают использоваться объекты D, G и H. Сборщик мусора пытается выполнить сборку памяти в поколении 0. Это приводит к следующему состоянию кучи:

B1 D1 G1 H1
Состояние памяти после сборки мусора в поколении 0

Хотя объект B прекратил свое существование, он остается в памяти, поскольку сборка мусора производилась только в поколении 0. Создаем еще пару объектов:

B1 D1 G1 H1 K0 L0 M0 N0
Создание в куче дополнительных объектов

Проходит еще какое-то время. Продолжают использоваться объекты D, G и L. Следующая сборка выполняется в поколениях 0 и 1. Состояние памяти после этой операции отображено в таблице.

D2 G2 L1
Состояние памяти после сборки мусора в поколениях 0 и 1

Завершители

Сборщик мусора поддерживает так называемые завершители (finalizers), которые чем-то напоминают деструкторы C++. В C# они также называются деструкторами и объявляются с тем же синтаксисом, что и деструкторы C++, но с точки зрения среды деструкторы C# являются завершителями.

Завершители позволяют выполнить необходимые операции перед тем, как объект будет освобожден сборщиком мусора, но они обладают рядом недостатков и потому используются редко. Как же работают завершители? При выделении памяти для объекта с завершителем среда включает ссылку на него в список объектов, которые нуждаются в завершении. Если в процессе сборки мусора выясняется, что на объект не существует ссылок, но он присутствует в списке завершения, объект помечается как готовый к завершению.

После окончания сборки мусора активизируется поток завершения, который вызывает завершители для всех готовых объектов. После вызова завершителя объект удаляется из списка объектов, нуждающихся в завершении, и может быть освобожден при следующей сборке мусора.

Следовательно, завершители обладают следующими недостатками:

  • Объекты с завершителями требуют дополнительных затрат ресурсов и дольше остаются в системе.
  • Завершение выполняется отдельным потоком.
  • Не существует гарантированного порядка вызова завершителей. Если объект a содержит ссылку на объект b и у обоих объектов определены завершители, завершитель объекта b может быть выполнен раньше завершителя объекта a, вследствие чего ссылка объект a к моменту завершения станет недействительной.

Все эти недостатки наглядно показывают, почему использовать деструкторы не рекомендуется.

Управление поведением сборщика мусора

В некоторых ситуациях бывает полезно управлять поведением сборщика мусора. При этом необходимо знать меру - вся сущность управляемой среды заключается в контроле над происходящими событиями, и любые изменения могут привести к возникновению проблем в других местах.

Форсированная сборка мусора

Функция System.GC.Collect() инициирует немедленную сборку мусора. Например, если программа только что закончила какие-то масштабные вычисления и освободила большое количество объектов, в этот момент разумно провести сборку мусора.

Запрет завершителей

Как упоминалось выше, экземпляр объекта, для которого определен завершитель, при создании помещается в список завершения. Если окажется, что надобность в вызове завершителя для объекта отпала (например, очистка была произведена специальной функцией), объект можно удалить из списка завершения функцией System.GC.SupressFinalize().


 
 
 

Библиотека программиста. 2009.
Администратор: admin@programmer-lib.ru