Найти тему

👀🗑Сборка мусора в Unity? Часть 1

В жизни каждого разработчика наступает момент, когда хочется разобраться, как что-то устроено изнутри. Уже не столько с практической точки зрения. А сколько становится интересным само по себе. Как это работает? А как работает под капотом?

(О основах работы сборщика мусора можно почитать самостоятельно, благо инфы много).


Какой GC использует Unity?

Многие думают, что тот самый, который например описан в CLR via C#.
На самом деле нет!

В юнити в качестве GC используется
Boehm garbage collector (Boehm GC), исходный код которого можно найти на GitHub.

Hans Boehm описывает работу этого GC следующим образом:

Коллектор использует алгоритм mark-sweep. Он обеспечивает инкрементальную и поколенческую сборку мусора в операционных системах, которые обеспечивают правильную поддержку виртуальной памяти


В любой реализации GC существует понятие «достижимый» объект. Если объект достижимый, значит на него кто-то ссылается.
Если же он не достижимый, то на него никто не ссылается, и он является претендентом на удаление.

Из описания Hans Boehm понимаем, что нужно узнать, что такое mark-sweep (отметить — подмести).
Он описан например в замечательной
книге дракона:

Изначально все корневые объекты добавляются как не сканированные.
— Далее первым циклом проходимся по всем дочерним элементам каждого корня. Если элемент является недостижимым, то помечаем.
— Вторым циклом очищаем все помеченные элементы.

Есть интересные модификации, советую ознакомиться.

Boehm GC —
консервативный сборщик мусора. Это значит, ему не требуется точная информация о расположении всех указателей объектов в памяти. Вместо этого он предполагает, что любое значение в памяти, которое может быть указателем на объект, является допустимым указателем.


Концептуально он работает примерно в 4 этапа:

  1. Подготовка. Каждый объект имеет связанный бит метки. GC чистит все биты метки. Указывает, что все объекты потенциально недоступны. (Unreached).
  2. Фаза Маркировки. Отмечает все объекты, к которым можно получить доступ через цепочки указателей на переменные. Часто сборщик не имеет реальной информации о расположении переменных-указателей в куче, поэтому он рассматривает все области статических данных, стеки и регистры как потенциально содержащие указатели. Любые битовые комбинации, представляющие адреса внутри объектов кучи, управляемых сборщиком, рассматриваются как указатели...
  3. Фаза Очистки. Сканирует кучу на наличие недоступных и, следовательно, немаркированных объектов и возвращает их в соответствующий свободный список для повторного использования. На самом деле это не отдельный этап; даже в неинкрементном режиме эта операция обычно выполняется по требованию во время выделения, при котором обнаруживается пустой список свободных мест. Таким образом, маловероятно, что фаза очистки затронет страницу, которая в любом случае не была бы затронута вскоре после этого.
  4. Фаза Финализации. Недоступные объекты, которые были зарегистрированы для финализации, ставятся в очередь для финализации вне сборщика.


Из сайта BDW -
Hans Boehm

Можно заметить, что модификация в алгоритме напоминает одно из улучшений mark-sweep с помощью
алгоритма Бейкера.
В книге дракона он описан примерно так:

  1. Изначально все объекты помечаются как недостижимые
  2. Проходимся от корней и ставим как достижимые все объекты, до которых мы добрались
  3. Оставшиеся недостижимые объекты — очищаются, помечаются свободными.


Все просто.

— Обратите внимание, в фазе маркировки Boehm GC отмечает все объекты, до которых может добраться из переменных-указателей (pointer variables). Поскольку обычно он не может определить, где расположены переменные-указатели, он сканирует следующие корневые сегменты на наличие указателей:
1. Регистры
2. Стек
3. Статические области памяти


Это только первая часть!
В следующей мы разберем инкрементную и поколенческую часть Boehm GC.


Подписывайтесь на telegram канал, чтобы не пропустить следующие части!👊

С подпиской рекламы не будет

Подключите Дзен Про за 159 ₽ в месяц