Циркулярные ссылки в Java

Учитывая совокупность экземпляров классов, которые ссылаются друг на друга сложным, циклическим образом: возможно ли, что сборщик мусора не сможет освободить эти объекты?

Я смутно припоминаю, что это было проблемой в JVM в прошлом, но я мысль это было решено много лет назад. Тем не менее, некоторое исследование в jhat показало, что причиной утечки памяти, с которой я сейчас столкнулся, является циклическая ссылка.

Примечание. Мне всегда казалось, что JVM способна разрешать циклические ссылки и освобождать такие «островки мусора» из памяти. Однако я задаю этот вопрос, чтобы посмотреть, не нашел ли кто-нибудь исключения.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
50
0
28 299
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Сборщик мусора знает, где находятся корневые объекты: статика, локальные переменные в стеке и т. д., И если объекты недоступны из корня, они будут восстановлены. Если они достижимы, то им нужно остаться.

Сборщик мусора - очень сложное программное обеспечение - оно было протестировано в огромном наборе тестов JCK. Это НЕ идеально, НО есть очень хороший шанс, что до тех пор, пока компилятор java (javac) будет компилировать все ваши классы, а JVM создаст его экземпляры, все будет хорошо.

Опять же, если вы удерживаете ссылки на корень этого графа объектов, память НЕ будет освобождена, НО если вы знаете, что делаете, все должно быть в порядке.

Нет, по крайней мере, используя официальную JVM Sun, сборщик мусора сможет обнаруживать эти циклы и освобождать память, как только больше не будет никаких внешних ссылок.

Просто чтобы усилить то, что уже было сказано:

Приложение, над которым я работал шесть лет, недавно изменилось с Java 1.4 на Java 1.6, и мы обнаружили, что нам приходилось добавлять статические ссылки на вещи, о которых мы даже не подозревали, что раньше можно было собирать мусор. Раньше нам не требовалась статическая ссылка, потому что раньше сборщик мусора был отстойным, а сейчас он стал намного лучше.

Если до него нельзя было добраться, как вы узнали, что он исчез? Интересно, есть ли какая-то ссылка, о которой вы не думаете, которая может заставить систему ее сохранить. Нить или слушатель.

Bill K 07.10.2008 04:26

Как вы узнали, собираются ли объекты сборщиком мусора, если вы, должно быть, отбросили все ссылки на них? Были ли это слабые ссылки, или у вас была ссылка на них через JNI, или это был какой-то живой объект, который можно собрать, даже если он все еще активен?

Brian 07.10.2008 04:27

Проблема заключалась в том, что когда мы обратились к нему, его уже не было. Я забыл точные детали, потому что это было несколько месяцев назад, но я думаю, что мы вызывали это через метод RMI.

Paul Tomblin 07.10.2008 04:33

Оказывается, утечка памяти может быть прослежена до класса, экземпляры которого хранятся в коллекции, а сам класс имеет ссылку (типа Object) на рассматриваемые объекты класса - нет, я не писал код для этого :-)

Ryan Delucchi 07.10.2008 09:12
Ответ принят как подходящий

Только в очень наивной реализации возникнут проблемы с циклическими ссылками. В Википедии есть хороший статья по различным алгоритмам сборки мусора. Если вы действительно хотите узнать больше, попробуйте (Amazon) Сборка мусора: алгоритмы автоматического управления динамической памятью. У Java был хороший сборщик мусора с версии 1.2 и исключительно хороший сборщик мусора в версиях 1.5 и Java 6.

Самая сложная часть улучшения GC - это сокращение пауз и накладных расходов, а не элементарных вещей, таких как циклическая ссылка.

Сборщики мусора с подсчетом ссылок печально известны этой проблемой. Примечательно, что Suns JVM не использует сборщик мусора с подсчетом ссылок.

Если объект не может быть достигнут из корня кучи (обычно, как минимум, через загрузчики классов, если ничто иное), то объекты будут уничтожены, поскольку они не копируются во время типичного Java GC в новую кучу.

Подсчет ссылок - очень хороший пример наивной реализации.

David G 07.10.2008 04:37

На самом деле, это своего рода умная реализация, это также довольно простая реализация, и, хотя у нее есть свои ограничения, это жизнеспособный алгоритм, особенно в средах с ограниченным объемом памяти.

Will Hartung 07.10.2008 07:09

Я читал о разработке MacOs, и, очевидно, ObjectiveC поддерживает как сборку мусора на основе подсчета ссылок, так и более сложную схему, аналогичную схеме Java.

Ryan Delucchi 07.10.2008 09:10

В спецификации Java сказано, что сборщик мусора может собирать мусор для вашего объекта. ТОЛЬКО если это недоступен из любого потока.

Доступный означает, что существует ссылка или цепочка ссылок, ведущая от A к B, и может пройти через C, D, ... Z независимо от того, что ему нужно.

JVM не собирает вещи для меня с 2000 года, но ваш опыт может отличаться.

Совет: сериализация Java кэширует объекты, чтобы сделать передачу объектной сетки более эффективной. Если у вас много больших временных объектов и вся ваша память перегружена, сбросьте сериализатор, чтобы очистить его кеш.

Если я правильно помню, то по спецификациям есть только гарантии того, что JVM не может собрать (все, что доступно), а не то, что он будет собирать.

Если вы не работаете с JVM в реальном времени, большинство современных сборщиков мусора должны уметь обрабатывать сложные ссылочные структуры и определять «подграфы», которые можно безопасно удалить. Эффективность, задержка и вероятность этого со временем улучшаются, поскольку все больше исследовательских идей внедряются в стандартные (а не исследовательские) виртуальные машины.

И чтобы подчеркнуть, что JVM, которая предполагает бесконечную кучу и никогда не собирает мусор, полностью совместима (хотя и неэффективна ;-)

ddimitrov 07.10.2008 04:56

Райан, судя по твоему комментарию к Циркулярные ссылки в Java, ты попал в ловушку ссылки на объекты из класса, который, вероятно, был загружен загрузчиком классов начальной загрузки / системы. На каждый класс ссылается загрузчик классов, который загрузил класс, и, таким образом, он может быть удален сборщиком мусора только в том случае, если загрузчик классов больше недоступен. Загвоздка в том, что загрузчик классов / системный загрузчик никогда не собирает мусор, поэтому объекты, доступные из классов, загруженных системным загрузчиком классов, тоже не могут быть собраны.

Причина такого поведения объясняется в JLS. Например, третье издание 12.7 http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.7.

На самом деле, мы думали, что мы это выяснили, но, к сожалению, теперь похоже, что у нас этого нет. Но да, это одна из возможностей, которую мы должны изучить.

Ryan Delucchi 08.10.2008 01:47

Циклическая ссылка происходит, когда один объект ссылается на другой, а этот другой ссылается на первый объект. Например:

class A {
private B b;

public void setB(B b) {
    this.b = b;
}
}

class B {
private A a;

public void setA(A a) {
    this.a = a;
}
}

public class Main {
public static void main(String[] args) {
    A one = new A();
    B two = new B();

    // Make the objects refer to each other (creates a circular reference)
    one.setB(two);
    two.setA(one);

    // Throw away the references from the main method; the two objects are
    // still referring to each other
    one = null;
    two = null;
}
}

Сборщик мусора Java достаточно умен, чтобы очищать объекты, если есть циклические ссылки, но больше нет живых потоков, которые имеют какие-либо ссылки на объекты. Таким образом, наличие такой круговой ссылки не приводит к утечке памяти.

Другие вопросы по теме