Циркулярные ссылки на Java - PullRequest
48 голосов
/ 07 октября 2008

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

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

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

Ответы [ 10 ]

42 голосов
/ 07 октября 2008

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

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

23 голосов
/ 07 октября 2008

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

12 голосов
/ 07 октября 2008

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

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

4 голосов
/ 07 октября 2008

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

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

3 голосов
/ 07 октября 2008

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

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

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

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

3 голосов
/ 07 октября 2008

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

2 голосов
/ 01 апреля 2013

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

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 достаточно умен, чтобы очистить объекты, если есть циклические ссылки, но нет живых потоков, которые больше не имеют ссылок на объекты. Таким образом, наличие такой круговой ссылки не приводит к утечке памяти.

2 голосов
/ 07 октября 2008

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

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

2 голосов
/ 07 октября 2008

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

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

0 голосов
/ 07 октября 2008

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

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

...