【发布时间】:2011-03-31 04:42:33
【问题描述】:
假设我有一个双向链表。我这样创建它:
MyList list = new MyList();
然后我添加一些节点,使用它,然后决定像这样丢弃旧列表:
list = new MyList();
由于我刚刚创建了一个新列表,旧内存区域内的节点仍然相互指向。这是否意味着具有旧节点的区域不会被垃圾收集?我是否需要让每个节点都指向 null 以便它们被 GC 处理?
【问题讨论】:
假设我有一个双向链表。我这样创建它:
MyList list = new MyList();
然后我添加一些节点,使用它,然后决定像这样丢弃旧列表:
list = new MyList();
由于我刚刚创建了一个新列表,旧内存区域内的节点仍然相互指向。这是否意味着具有旧节点的区域不会被垃圾收集?我是否需要让每个节点都指向 null 以便它们被 GC 处理?
【问题讨论】:
不,你没有。 Java GC 可以很好地处理循环引用。
从概念上讲,每次 GC 运行时,它都会查看系统中所有“活动”的根引用:
Class 对象引用的,而这些对象又是由 ClassLoaders 引用的,但我们暂时忽略它。)对于那些“已知的活动”对象,它会检查其中的字段,并将其添加到列表中。它向下递归到那些引用的对象,依此类推,直到找到系统中的每个活动对象。然后它会垃圾收集它没有认为是活的所有东西。
您的循环引用节点相互引用,但没有活动对象引用它们,因此它们有资格进行垃圾回收。
请注意,这是对垃圾收集器概念上工作原理的粗略简化总结。实际上,它们非常复杂,包括生成、压缩、并发问题等等。
【讨论】:
垃圾收集器查找没有在任何地方引用的对象。 因此,如果您创建了一个对象并且像示例一样丢失了引用,那么垃圾收集器将收集它。
【讨论】:
不——Java(至少正常实现)不使用引用计数,它使用真正的垃圾收集器。这意味着(本质上)当它耗尽内存时,它会查看堆栈、寄存器和其他始终可访问的位置上的指针,并“追逐”它们以找到可从中访问的所有内容。
其他数据结构(如双向链表)中的指针根本无关紧要,除非有一些外部指针(可访问)指向它们。
【讨论】:
不,GC 无论如何都会回收它们,因此您无需将它们指向 null。这是来自 JavaWorld article 的一段很好的描述:
任何垃圾收集算法都必须 做两件基本的事情。首先,它必须 检测垃圾对象。其次,它 必须回收使用的堆空间 垃圾对象并制作它 可用于程序。垃圾 检测通常完成 通过定义一组根和 从确定可达性 根。一个对象是可达的,如果有 是一些参考路径 执行程序的根 可以访问对象。根是 程序始终可以访问。任何 可从 根被认为是活的。对象 不可达的被认为是 垃圾,因为他们不能再 影响程序的未来进程 执行。
【讨论】:
垃圾收集器查看对象是否被活动线程引用。如果任何活动线程都无法访问对象,则它们有资格进行垃圾回收。
对象是否相互引用无关紧要。
【讨论】:
正如其他人所指出的,Java 垃圾收集器不仅仅关注引用计数;它还关注引用计数。相反,它本质上是查看一个图,其中节点是当前存在的对象,链接是从一个对象到另一个对象的引用。它从一个已知处于活动状态的节点(例如 main 方法)开始,然后垃圾收集任何无法到达的节点。
The Wikipedia article on garbage collection 讨论了各种可以做到这一点的方法,尽管我不知道任何 JVM 实现都使用哪种方法。
【讨论】:
如果您创建了自己的双链表,并且您在此双链表中放入了容器(包含列表中的项目);只有那些容器相互链接。
因此,在您的列表中,您将在 A' 中包含一个对象 A。 A' 链接到 B' 并且 B' 是一个容器,其中包含 B 等。并且没有一个对象必须引用另一个对象。
在正常情况下,这些容器不会从外部获得(只有内容是有趣的);所以只有您的列表才会引用您的容器(请记住,您的内容不知道他的容器)。
如果您删除对列表的最后引用(列表,而不是容器或内容),GC 将尝试收集您的列表内容,巫婆是您的容器和您的内容。
由于您的容器在外部不可用,它们唯一的参考是彼此和主列表。所有这些都被称为孤岛。关于内容,如果它们在您的应用程序中仍有引用,它们将在 GC 中存活,否则它们不会。
因此,当您删除列表时,只会删除 A' 和 B',因为即使它们仍然有引用,这些引用也是孤岛的一部分。如果 A 和 B 没有更多引用,它们也将被删除。
【讨论】: