Сборка мусора с C# (Garbage collector)

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

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

  • Ссылки на глобальные объекты (В C# их нет, но в CIL их размещение допускается);
  • Ссылки на статические объекты либо поля;
  • Ссылки на локальные переменные;
  • Ссылки на объектные параметры, передаваемые методу;
  • Ссылки на объекты, ожидающие финализации;
  • Регистр центрального процессора, который ссылается на объект;

Во время сборки мусора исполняющая среда будет исследовать объекты в управляемой куче, чтобы определить, являются ли они по прежнему достижимы (корневыми) для приложения.  Для этого CLR будет строить граф объектов, который представляет каждый достижимый объект в куче.
Например, у нас есть управляемая куча с набором элементов A, B, C, D, E, F, G, I.
 
В данном примере недоступными оказались объекты C, F, I. Данные объекты будут удалены из памяти, оставшееся пространство в куче сжимается, указатель на следующий элемент настраивается таким образом, чтобы указывать на следующий доступный участок памяти. Конечный результат будет следующим:
 
Стоит уточнить, что существует куча для больших объектов (более 85000 байт). К большим объектам не применяется сжатие. О них я расскажу позже.


Думаю, теперь понятно, какие объекты CLR будет считать недостижимыми. Однако нужно понимать, что если бы каждый раз проверялся каждый находящийся в куче объект, то это заняло бы массу времени и вся польза от автоматической сборки мусора могла бы вытесняться постоянно тормозящими приложениями. Для оптимизации данного процесса был придуман механизм «поколений».
Каждый объект в куче соответствует одному из 3 поколений.

  • Поколение 0: Новый размещенный в куче объект, который еще никогда не помечался как подлежащий сборке мусора.
  • Поколение 1: Объект, который уже пережил один процесс сборки мусора.
  • Поколение 2: Объект, который пережил более одного процесса сборки мусора.

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

  • Заполнено поколение 0;
  • Вызов из кода GC.Collect;
  • Операционная система сообщает, что недостаточно памяти;

Теперь рассмотрим подробно отличия работы с большими объектами (больше 85000 байт)

  • Память для больших объектов выделяется в отдельной части адресного пространства;
  • К большим объектам не применяется сжатие (в будущих версиях CLR возможно наличие сжатия)
  • Большие объекты всегда считаются частью поколения 2;

Существует возможность программного управления сборщиком мусора. Для этого существует тип System.GC
Чтобы заставить уборщика мусора провести уборку, достаточно вызвать метода Collect, доступный в классе GC. При вызове также можно указать номер поколения, в котором необходимо произвести уборку мусора.
Желательно избегать вызовов любых методов Collect. Лучше не вмешиваться в работу сборщика мусора пока без этого можно обойтись.

Комментарии (2) -

Объяснено доступным языком, я понял. Спасибо))

Спасибо! все доступно и понятно Smile

Добавить комментарий