Java中的循环引用

时间:2008-10-06 23:59:45

标签: java memory memory-management memory-leaks garbage-collection

给定一个以复杂,循环,方式相互引用的类实例的聚合:垃圾收集器是否可能无法释放这些对象?

我模糊地回忆起这在过去是JVM中的一个问题,但我认为这是多年前解决的。然而,在jhat的一些调查显示,循环引用是我现在面临的内存泄漏的原因。

注意:我一直认为JVM能够解析循环引用并从内存中释放出这些“垃圾岛”。但是,我提出这个问题只是为了看看是否有人发现了任何例外情况。

10 个答案:

答案 0 :(得分:42)

只有非常天真的实现才会出现循环引用问题。维基百科对不同的GC算法有很好的article。如果您真的想了解更多信息,请尝试(亚马逊)Garbage Collection: Algorithms for Automatic Dynamic Memory Management 。自1.2以来,Java有一个很好的垃圾收集器,在1.5和Java 6中有一个非常好的垃圾收集器。

改进GC的难点在于减少暂停和开销,而不是像循环引用这样的基本内容。

答案 1 :(得分:23)

垃圾收集器知道根对象的位置:静态,堆栈上的本地等,如果无法从根目录访问对象,则它们将被回收。如果他们可以到达,那么他们需要坚持下去。

答案 2 :(得分:12)

Ryan,根据您对Circular References in Java的评论判断,您陷入了引用类中的对象的陷阱,该类可能由引导程序/系统类加载器加载。每个类都由加载类的类加载器引用,因此只有在类加载器不再可访问时才能进行垃圾收集。问题是bootstrap / system类加载器从不被垃圾收集,因此,从系统类加载器加载的类可以访问的对象也不能被垃圾收集。

在JLS中解释了此行为的原因。例如,第三版12.7 http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.7

答案 3 :(得分:4)

如果我没记错的话,那么根据规范,只能保证JVM无法收集的内容(任何可以访问的内容),而不是它将收集的内容。

除非您正在使用实时JVM,否则大多数现代垃圾收集器应该能够处理复杂的参考结构并识别可以安全消除的“子图”。随着越来越多的研究思路进入标准(而非研究)VM,效率,延迟和这种可能性随着时间的推移而不断提高。

答案 4 :(得分:3)

不,至少使用Sun的官方JVM,垃圾收集器将能够检测到这些周期,并在不再有来自外部的任何引用时释放内存。

答案 5 :(得分:3)

Java规范说垃圾收集器可以垃圾收集你的对象 仅限无法从任何线程访问

Reachable意味着有一个从A到B的引用或引用链, 并且可以通过C,D,...... Z来表达它所关心的一切。

自2000年以来,JVM没有收集东西对我来说不是问题,但你的里程可能会有所不同。

提示:Java序列化缓存对象以使对象网格传输高效。如果你有许多大型瞬态对象,并且所有内存都被占用,请重置序列化程序以清除它的缓存。

答案 6 :(得分:2)

只是放大已经说过的话:

我已经工作了六年的应用程序最近从Java 1.4改为Java 1.6,我们发现我们必须添加静态引用,我们甚至没有意识到它们是垃圾可收集的。之前我们不需要静态引用,因为垃圾收集器曾经很糟糕,而且它现在好多了。

答案 7 :(得分:2)

参考计数GC因此问题而臭名昭着。值得注意的是,Suns JVM不使用引用计数GC。

如果无法从堆的根目录访问该对象(通常,至少通过类加载器,如果没有其他内容0),那么对象将被销毁,因为它们在典型的Java GC期间不会被复制到新堆中

答案 8 :(得分:2)

当一个对象引用另一个对象,而另一个对象引用第一个对象时,会发生循环引用。例如:

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的垃圾收集器足够聪明,可以清理对象,但是没有活动线程可以再次引用这些对象。因此,像这样的循环引用不会产生内存泄漏。

答案 9 :(得分:0)

垃圾收集器是一个非常复杂的软件 - 它已经在一个巨大的JCK测试套件中进行了测试。它并不完美但是很有可能只要java编译器(javac)将编译所有类并且JVM将实例化它,那么你应该很好。

然后再次,如果您持有对该对象图的根的引用,则不会释放内存但是如果您知道自己在做什么,那么应该没问题。