Java中的循环引用

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

我隐约记得过去这是JVM中的一个问题,但是我认为这在多年前就已经解决了。 然而,一些调查显示,我现在正面临一个循环引用,就是造成内存泄露的原因。

注:我一直觉得JVM有能力解决循环引用,从内存中释放这些“垃圾岛”。 不过,我只是提出这个问题,看是否有人发现任何例外。

只有一个非常天真的实现会有循环引用的问题。 维基百科有不同的GCalgorithm的好文章 。 如果你真的想了解更多,请尝试(Amazon) 垃圾回收:自动dynamic内存pipe理algorithm 。 Java从1.2开始就有了一个很好的垃圾回收器,而且在1.5和Java 6中也是非常好的垃圾回收器。

改进GC的难点在于减less停顿和开销,而不是循环引用等基本的东西。

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

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

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

如果我没有记错的话,那么根据规范, 只能保证JVM不能收集什么东西(任何可达的东西),而不是收集的东西。

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

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

Java规范说,垃圾收集器可以只垃圾收集你的对象如果它不能从任何线程到达。

可达意味着有从A到B的引用或引用链,并且可以通过C,D,… Z去关注它。

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

提示:Java序列化caching对象以使对象网格传输高效。 如果你有很多大的临时对象,并且你所有的内存都被占用,重置你的序列化程序来清除它的caching。

只是为了扩大已经说过的话:

我已经工作了六年的应用程序最近从Java 1.4变成了Java 1.6,而且我们发现我们必须添加静态引用,以前我们甚至没有意识到这些引用是垃圾收集的。 之前我们并不需要静态引用,因为垃圾回收器用来吸引,现在就好多了。

引用计数GC是臭名昭着的这个问题。 值得注意的是,太阳JVM不使用引用计数GC。

如果无法从堆的根目录(通常至less通过类加载器(如果没有其他else))访问该对象,则在典型Java GC期间不会将对象复制到新堆中。

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

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

垃圾收集器是一个非常复杂的软件 – 它已经在一个巨大的JCKtesting套件中进行了testing。 这是不完美的,但是有一个很好的机会,只要java编译器(javac)将编译所有的类,JVM将实例化它,那么你应该是好的。

然后,如果你持有对这个对象图的根的引用,内存将不会被释放,但如果你知道你在做什么,你应该没问题。