Частый вопросы - и довольно стандартный ответ "Он проверяет, ссылается ли что-то на объект. Если нет, то ставит в очередь на удаление".
Но есть более каверзный вопрос, который в последнее время спрашивают довольно часто "Если у нас есть объект A, который ссылается на объект B и объект B, который ссылается на объект A - удалит ли GC эти объекты?".
Казалось бы, всё очевидно. Если GC действительно работает исключительно по ссылкам, то при такой циклической зависимости, у нас объекты не удалятся.
Оказывается нет, потому что GC работает не только по ссылкам, но и по корням.
Корни - это точки входа для поиска живых объектов: локальные переменные в стеке, регистры, статические поля, GC-handle’ы и т.п. GC начинает обход именно от этих корней и идёт по outgoing ссылкам (от объекта к тем, на кого он ссылается). Если никакой корень не ведёт к группе объектов, то эта группа останется не посещённой и будет считаться мусором - даже если внутри группы объекты сильно ссылаются друг на друга.
Раскроем детальнее, что такое outgoing и incoming ссылки.
Outgoing ссылки - это ссылки, которые объект хранит в своих полях/элементах массива и которыми он указывает на другие объекты.
Пример:
class Node { public Node Other; public string Name; }
var a = new Node();
var b = new Node();
a.Other = b; // это outgoing ссылка из a на b
a.Name = "A"; // это поле - не ссылка (если бы Name был object/классом, это был бы outgoing)Incoming ссылки - это ссылки на сам объект, которые хранят другие объекты или корни.
Пример:
class Owner { public Target T; }
class Target { }
var owner = new Owner();
var t = new Target();
owner.T = t; // здесь owner.T - incoming ссылка для объекта tКорни - это набор входных точек в граф объектов (локальные переменные, регистры, статические поля, GC-handles, очередь финализаторов и т.д.). GC начинает обход от этих точек и помечает всё достижимое. То, до чего не дошёл - считается недостижимым и может быть собран.
GC начинает с корней и идёт по outgoing ссылкам: он посещает объект, смотрит его поля (outgoing) и переходит к объектам, на которые они указывают. GC не перебирает incoming ссылки напрямую - он просто обнаружит incoming, когда придёт по outgoing ссылке от некоторого уже помеченного объекта.
Поэтому зацикл A и B (A.Outgoing -> B и B.Outgoing -> A) без корней останется непомеченным: у GC нет входной точки, чтобы пойти по outgoing к этим объектам.
Таким образом, ответ - да. GC соберёт эти объекты.
🚀 Пост Guru Unity: @Minerope
