Если вы думаете об объектах в памяти как о дереве, «корнями» будут корневые узлы - каждый объект, непосредственно доступный вашей программе.
Person p = new Person();
p.car = new Car(RED);
p.car.engine = new Engine();
p.car.horn = new AnnoyingHorn();
Есть четыре объекта; человек, красная машина, ее двигатель и гудок. Нарисуйте контрольный график:
Person [p]
|
Car (red)
/ \
Engine AnnoyingHorn
И вы получите Person
в «корне» дерева. Он активен, потому что на него ссылается локальная переменная p
, которую программа может использовать в любое время для ссылки на объект Person
. Это также относится и к другим объектам, через p.car
, p.car.engine
и т. Д.
Поскольку Person
и все другие объекты, рекурсивно связанные с ним, являются живыми, возникнет проблема, если GC соберет их.
Учтите, однако, если через некоторое время выполняется следующее:
p.car = new Car(BLUE);
И перерисовать график:
Person [p]
|
Car (blue) Car (red)
/ \
Engine AnnoyingHorn
Теперь Person
доступен через p
, а синяя машина - через p.car
, но нет никакой возможности снова получить доступ к красной машине или ее частям - они не подключены к живому корню. Их можно безопасно собрать.
Так что это действительно вопрос выбора каждой начальной точки (каждой локальной переменной, глобальных переменных, статики, всего в других потоках и фреймах стека) - каждого корня - и рекурсивного следования всем ссылкам, чтобы составить список всех "живых" "объекты: объекты, которые используются и не подходят для удаления. Все остальное - мусор, ожидающий сбора.