有一个具有广泛对象图的应用程序。该图主要由一组子图组成,这些子图通过唯一的引用与图的其余部分相连。但是,在每个这样的子图内部,对象之间存在一些交叉引用。偶尔需要丢弃这样的子图。仅仅将指向该子图的唯一引用设置为null就足以使其有资格进行垃圾收集吗?
我的担忧是,内部的交叉引用可能会“保护”整个子图免受垃圾收集的影响。换句话说,垃圾收集器是否足够聪明,能够确定子图中的所有引用都不会离开子图的边界,因此可以清除整个子图。
有一个具有广泛对象图的应用程序。该图主要由一组子图组成,这些子图通过唯一的引用与图的其余部分相连。但是,在每个这样的子图内部,对象之间存在一些交叉引用。偶尔需要丢弃这样的子图。仅仅将指向该子图的唯一引用设置为null就足以使其有资格进行垃圾收集吗?
我的担忧是,内部的交叉引用可能会“保护”整个子图免受垃圾收集的影响。换句话说,垃圾收集器是否足够聪明,能够确定子图中的所有引用都不会离开子图的边界,因此可以清除整个子图。
正如这个SO问题中所述,循环引用得到了很好的管理。
Java不使用引用计数,而是使用追踪垃圾回收(例如标记清除、复制回收或两者的组合)。它遵循所有活动引用以找出哪些对象是“可达”的,然后清理其他所有东西。
对象中的引用本身不可达不影响可达性,因此它们是否为null并不重要。
唯一可能具有重大影响的情况是在长时间运行的方法中丢弃一个非常大的对象。
在这种情况下,将图的引用设置为null将有助于创建一个孤立岛(即使是内部循环引用),如这篇文章中所述。
您可以在Garbage Collection的真相中找到更多关于不可达状态的详细信息:
不可达
当没有更多的强引用指向对象时,该对象将进入不可达状态。
当一个对象不可达时,它成为被回收的候选对象。
请注意措辞:
仅因为一个对象是可回收的候选对象,并不意味着它会立即被回收。JVM可以延迟回收,直到有一个立即需要使用该对象占用的内存。
重要的是要注意,不只是任何强引用都会使对象保留在内存中。这些必须是从垃圾回收根引用链传递的引用。GC根是一个特殊类别的变量,包括:
循环强引用不一定会导致内存泄漏。 考虑创建两个对象并将它们互相引用的代码。
public void buidDog() {
Dog newDog = new Dog();
Tail newTail = new Tail();
newDog.tail = newTail;
newTail.dog = newDog;
}
在方法返回之前,临时堆栈变量中存在对buildDog
方法中的Dog
和Tail
的强引用。
在buildDog
方法返回之后,Dog
和Tail
都无法从根对象访问,并成为可回收的对象(虚拟机可能不会实际收集这些对象的时间是不确定的)。
是的 - 垃圾收集器可以处理循环引用等情况。