Насколько я понимаю, сборка мусора в Java очищает некоторые объекты, если ничто другое не «указывает» на этот объект.
Мой вопрос: что произойдет, если у нас будет что-то вроде этого:
class Node {
public object value;
public Node next;
public Node(object o, Node n) { value = 0; next = n;}
}
//...some code
{
Node a = new Node("a", null),
b = new Node("b", a),
c = new Node("c", b);
a.next = c;
} //end of scope
//...other code
a
, b
и c
должны быть сборщиком мусора, но на них все ссылаются другие объекты.
Как Java-сборка мусора справляется с этим? (или это просто утечка памяти?)
java
garbage-collection
AlexeyMK
источник
источник
Ответы:
GC Java считает объекты «мусором», если они недоступны через цепочку, начинающуюся с корня сборки мусора, поэтому эти объекты будут собираться. Даже если объекты могут указывать друг на друга, образуя цикл, они все равно остаются мусором, если они отрезаны от корня.
См. Раздел о недоступных объектах в Приложении A: Правда о сборке мусора в производительности платформы Java: стратегии и тактики для более подробной информации.
источник
да Java Сборщик мусора обрабатывает циклическую ссылку!
Существуют специальные объекты, называемые корнями сборки мусора (GC-корнями). Они всегда достижимы, как и любой объект, который имеет их в своем корне.
Простое Java-приложение имеет следующие корни GC:
Чтобы определить, какие объекты больше не используются, JVM периодически запускает так называемый алгоритм метки и развертки . Работает следующим образом
Таким образом, если какой-либо объект недоступен из корней GC (даже если он самоссылочный или циклически), он будет подвергнут сборке мусора.
Иногда это может привести к утечке памяти, если программист забудет разыменовать объект.
Источник: Java Memory Management
источник
Сборщик мусора начинается с некоторого «корневого» набора мест, которые всегда считаются «достижимыми», таких как регистры ЦП, стек и глобальные переменные. Это работает, находя любые указатели в этих областях, и рекурсивно находя все, на что они указывают. Как только все это найдено, все остальное - мусор.
Вариантов, конечно, немало, в основном ради скорости. Например, большинство современных сборщиков мусора являются «поколенными», что означает, что они делят объекты на поколения, и, когда объект стареет, сборщик мусора проходит все больше и больше между попытками выяснить, действителен ли этот объект или нет. - он просто начинает предполагать, что, если он прожил долгое время, вполне вероятно, что он будет жить еще дольше.
Тем не менее, основная идея остается прежней: все это основано на том, чтобы начинать с некоторого корневого набора вещей, который он принимает как должное, все еще можно использовать, а затем преследовать все указатели, чтобы найти то, что еще может быть использовано.
Интересно отметить: могут ли люди часто удивляться степени сходства между этой частью сборщика мусора и кодом для маршалинга объектов для таких вещей, как удаленные вызовы процедур. В каждом случае вы начинаете с некоторого корневого набора объектов и гоняетесь за указателями, чтобы найти все другие объекты, которые относятся к ...
источник
Ты прав. Конкретная форма сборки мусора, которую вы описываете, называется « подсчет ссылок ». То, как это работает (по крайней мере, концептуально, по крайней мере, большинство современных реализаций подсчета ссылок на самом деле реализованы совсем по-другому), в простейшем случае выглядит так:
И эта простая стратегия имеет именно ту проблему, которую вы описываете: если A ссылается на B и B ссылается на A, то оба их счетчика ссылок не могут быть меньше 1, что означает, что они никогда не будут собраны.
Существует четыре способа решения этой проблемы:
Кстати, другой важный способ реализации сборщика мусора (о котором я уже упоминал пару раз выше) - это трассировка . Коллектор отслеживания основан на концепции достижимости . Вы начинаете с некоторого корневого набора, который, как вы знаете, всегда доступен (например, глобальные константы или
Object
класс, текущая лексическая область действия, текущий кадр стека), и оттуда вы отслеживаете все объекты, которые достижимы из корневого набора, затем все объекты, которые достижимы из объектов, достижимых из корневого набора и т. д., пока у вас не будет транзитивного замыкания. Все, что не находится в этом закрытии, является мусором.Поскольку цикл доступен только внутри самого себя, но не доступен из корневого набора, он будет собран.
источник
Java GC на самом деле ведут себя не так, как вы описали. Точнее сказать, что они начинаются с базового набора объектов, часто называемых «корнями GC», и будут собирать любой объект, который не может быть достигнут из корня.
Корни GC включают в себя такие вещи, как:
Итак, в вашем случае, когда локальные переменные a, b и c выйдут из области видимости в конце вашего метода, больше не будет корней GC, которые прямо или косвенно содержат ссылку на любой из ваших трех узлов, и они будут иметь право на сбор мусора.
Ссылка на TofuBeer содержит более подробную информацию, если вы этого хотите.
источник
Эта статья (более недоступная) подробно описывает сборщик мусора (концептуально ... существует несколько реализаций). Соответствующая часть вашего сообщения "A.3.4 Недоступен":
источник
Сборка мусора обычно не означает «очистить некоторый объект, если ничто другое не« указывает »на этот объект» (это подсчет ссылок). Сборка мусора примерно означает поиск объектов, которые не могут быть достигнуты из программы.
Таким образом, в вашем примере, после того, как a, b и c выйдут из области видимости, они могут быть собраны GC, поскольку вы больше не можете получить доступ к этим объектам.
источник
Билл ответил на ваш вопрос напрямую. Как сказал Амнон, ваше определение сборки мусора - просто подсчет ссылок. Я просто хотел добавить, что даже очень простые алгоритмы, такие как разметка, очистка и копирование, легко обрабатывают циклические ссылки. Так что в этом нет ничего волшебного!
источник