Как стать автором
Обновить

Сборщик мусора в среде .NET

Время на прочтение5 мин
Количество просмотров74K
Здравствуйте, Великие и Ужасные хабражители!
Как я недавно узнал, не так много народу знает о том, как работает сборщик мусора. Хотя я понимаю, что 99% разработчиков это не особо нужно, но я хотел бы, чтобы все, кто разрабатывают приложения на .NET, знали принцип его работы. В этой статье я постараюсь вкратце рассказать, как собственно работает сборщик мусора.

Базовые сведения о времени жизни объекта


Как известно, при инициализации объекта в памяти выделяет нужное под объект место. Использование ключевого слова new приводит к добавление объекта класса в так называемую управляемую кучу, а назад возвращается ссылка на объект.
При создании приложений на C# можно смело полагать, что исполняющая среда .NET будет сама заботиться об управляющей куче без непосредственного вмешательства со стороны программиста. На самом деле "золотое правило" по управлению памятью звучит так:
Размещайте объект в управляющей куче с помощью ключевого слова new и забывайте об этом.


После создания объект будет автоматически удалён сборщиком мусора, когда в нём отпадёт надобность. Сразу возникает вопрос о том, каким образом сборщик мусора определяет, когда в объект отпадает необходимость?

Давайте просмотрим простой пример:
public static void MakeACar()
{
        // Если myCar является единственной ссылкой на объект
        // Car, тогда при возврате результата данных
        // методом объекта Car *может* быть уничтожен.
        Car myCar = new Car();
        ...
}


Обратите внимание, что ссылка на объект Car(myCar) была создана непосредственно внутри метода MakeACar() и не передавалась за пределы определяющей области действия (ни в виде возвращающегося значения, ни в виде параметров ref/out). По этому после вызова метода ссылка на myCar окажется недостижимой, а объект Car — кандидатом на удаление сборщиком мусора. Следует, однако, понимать, что нет никакой гарантии на удаление объекта сразу после выполнение метода MakeACar(). Всё, что в данный момент можно предполагать, так это то, что когда в CLR-среде будет в следующий раз проводиться сборка мусора, то объект myCar будет поставлен на удаление.

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

Роль корневых элементов приложения


Чтобы разобраться, каким образом сборщик мусора определяется, когда объект уже не нужен, необходимо знать, что собой представляют корневые элементы приложения (application roots). Попросту говоря, корневым элементом (root) называется ячейка в памяти, в которой содержится ссылка на размещающийся в куче объект. Строго говоря, корневыми могут называться элементы:
  • Ссылки на глобальные объекты (хотя в C# они не разрешены, CIL-код позволяет размещать глобальные объекты
  • Ссылки на любые статические объекты или статические поля.
  • Ссылки на локальные объекты в пределах кодовой базы приложения.
  • Ссылки на передаваемые методу параметры объекта.
  • Ссылки на объект, ожидающий финализации.
  • Любые регистры центрального процессора, которые ссылаются на объект.


Во время процессы сборки мусора исполняющая среда будет исследовать объекты в куче, чтобы определить, являются ли они по прежнему достижимыми (т.е. корневыми) для приложения. Для этого среда CLR будет создавать графы объектов, представляющие все достижимые для приложения объекты. Кроме того, следует иметь ввиду, что сборщик мусора никогда не будет создавать граф для одного и того же объекта дважды, избегая необходимости выполнения подсчёта циклических ссылок, который характерен для программирования в среде COM.

Поколения объектов

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

Например, класс, определённый в главном окне настольного приложения, будет оставаться в памяти вплоть до завершения программы. С другой стороны, объект, попавший в кучу совсем недавно (например те, что находятся в области видимости методов), вероятнее всего будут становиться недостижимыми достаточно быстро. Изводя из этих предположений, каждый объект в куче относится к:
  • Поколение 0. Идентифицируется новый только что размещённый объект, который ещё никогда не помечался как надлежащий удалению в процессе сборки мусора
  • Поколение 1. Идентифицирует объект, который уже «пережил» один процесс сборки мусора (был помечен, как надлежащий удалению, но не был удалён из-за достаточного свободного места в куче).
  • Поколение 2. Идентифицирует объект, который пережил более одного прогона сбора мусора


Поколения 0 и 1 называются эфемерными.

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

Параллельная сборка мусора в .NET 1.0 — .NET 3.5

До выхода .NET 4.0 очистка неиспользуемых объектов проводилась с применением техники параллельной сборки мусора. В этой модели при выполнении сбора мусора эфемерных объектов сборщик мусора временно приостанавливал все активные потоки внутри текущего процесса, чтобы приложение не могло получить доступ к управляемой куче вплоть до завершения процесса сборки мусора.
По завершению цикла сборки мусора приостановленным потокам разрешалось снова продолжить работу. К счастью, в .NET 3.5 сборщик мусора был хорошо оптимизирован и потому связанные с ним короткие перерывы в работе с приложением редко становились заметными.
Как и оптимизация, параллельная сборка мусора позволяла проводить очистку объектов, которые не были обнаружены ни в одном из эфемерных поколений, в отдельном потоке. Это сокращало (но не устраняло) необходимость в приостановке активных потоков исполняющей средой .NET. Тем более, параллельная сборка мусора позволяла размещать объекты в куче во время сборки объектов не эфемерных поколений.

Фоновая сборка мусора в .NET 4.0

И напоследок я хотел бы вам рассказать об улучшении работы сборщика мусора в .NET 4.0.

В .NET 4.0 сборщик мусора по-другому решает вопрос о приостановке потоков и очистке объектов в управляемой куче, используя при этом технику фоновой сборки мусора. Не смотря на её название, это вовсе не означает, что вся сборка мусора теперь происходит в дополнительных фоновых потоках выполнения. На самом деле в случае фоновой сборки мусора для объектов, не относящихся к эфемерному поколению, исполняющая среда .NET теперь может проводить сборку мусора объектов эфемерного поколения в отдельном фоновом потоке.
Механизм сборки мусора в .NET 4.0 был улучшен так, чтобы на приостановку потока, связанного с деталями сбора мусора, требовалось меньше времени. Благодаря этим изменениям, процесс очистки неиспользуемых объектов поколения 0 и 1 стал оптимальным. Он позволяет получать более высокий уровень производительности приложений.

Заключение

В заключении я хотел бы сказать, что сборщик мусора, который используется в .NET 4.0 позволяет оптимизировать работу программ и практически не влияет на производительность.
В следующей статье я расскажу, как можно собственноручно управлять процессом сборки мусора с помощью пространства имён System.GС.

Более подробно: здесь
Теги:
Хабы:
Всего голосов 105: ↑79 и ↓26+53
Комментарии77

Публикации