"Как работает сборщик мусора?"
Этот вопрос можно услышать на каждом собеседовании. Поэтому важно знать, как на него отвечать.
Сборщик мусора (Garbage Collector или GC) - это инструмент для автоматического управления памятью в CLR.
GC нужен для того, чтобы не управлять памятью вручную. Вместо этого CLR позаботиться о том, чтобы память была очищена своевременно.
🔄 Сборка мусора в SOH
Основные причины для сборки мусора:
- Закончилась свободная память в Small Object Heap
- Закончилась свободная память в Large Object Heap
- Ручной вызов GC
При их наступлении начинается следующий процесс:
- Все управляемые потоки встают на паузу.
- Поток, который вызвал GC запускает процедуру сборки.
- Выбирается поколение. Мусор будет собран в нем и более молодых поколениях.
- Фаза маркировки.
- Фаза планирования.
- Фаза сборки мусора.
- Восстанавливается работа всех потоков.
Для LOH процесс аналогичен, но не будет фазы планирования и стратегии Compact.
🏷 Фаза маркировки
Сборщик мусора проверяет наличие в куче неиспользуемых приложением объектов, чтобы освободить занятую ими память. Неиспользуемым считается объект, на который не осталось ссылок из корней (roots) приложения.
Корнем называется адрес указателя на объект ссылочного типа:
- Локальные переменные метода
- Параметры метода
- Статические поля типов
- Корни из очереди финализации (Finalization roots)
Корнем могут быть переменные только ссылочного типа.
При запуске сборщик мусора считает, что все объекты в куче - мусор. То есть на них нет никаких ссылок из корней.
Сборщик проходит по стеку потока и проверяет все корни. В системное поле объекта, Type Handle, проставляется бит, если существует корень, который ссылается на объект. Это и есть признак маркировки объекта.
Если маркированный объект ссылается на другой объект в куче, он тоже маркируется. GC узнает о ссылках на другие объекты через таблицу виртуальных методов, на которую ссылается Type Handle.
Сборщик пропускает объект, если он уже промаркирован.
После выполнения маркировки сборщик "знает", на какие объекты есть ссылки из корней приложения.
📋 Фаза планирования
После фазы маркировки GC необходимо выбрать стратегию сборки мусора:
- Sweep Collection: все недостижимые объекты поколения считаются свободным местом. Все достижимые объекты становятся более старшим поколением путем сдвига границы поколений.
- Compact Collection: все достижимые объекты поколения уплотняются, занимая места недостижимых объектов и становятся более старшим поколением. По сути, сдвигаются влево.
В фазу планирования выполняется виртуальная сборка мусора, объединяющая обе стратегии. Это возможно, так как объекты промаркированы. В объекты, подлежащие сборке, записываются данные о смещении соседнего немаркированного объекта в случае Compact на основании известных размеров объектов. Таким образом в результате работы виртуальной сборки мусора известно значение фрагментации в случае Sweep, а так же данные о сжатии в случае Compact.
Если фрагментация памяти после Sweep будет высокой, то выбирается Compact. Иначе - Sweep.
🧹 Фаза сборки
На основе выбранной стратегии выполняется сборка мусора, а именно физический коммит результатов работы виртуальной сборки мусора предыдущей фазы.
Для sweep сборщик обходит кучу линейно и очищает немаркированные объекты.
Для compact сборщик проходит кучу линейно и ищет непрерывные блоки немаркированных объектов. Небольшие блоки пропускаются, а в больших непрерывных блоках маркированные объекты перемещаются влево, сжимая кучу. При этом правая граница вышестоящего поколения сдвигается вправо.
Сдвиг объектов в памяти делает недействительными их адреса, поэтому сборщик должен проверить все корни и обновить их, чтобы они ссылались на новые. Если объект содержит поле, указывающее на другой перемещенный объект, сборщик должен исправить и их.
🎯 Выводы
Теперь ты знаешь, как работает сборщик мусора. Успехов на собеседованиях!
#техничка