ChatGPT解决这个技术问题 Extra ChatGPT

垃圾收集器和循环引用

考虑这两个类:

public class A
{
     B b;
     public A(B b) { this.b = b; }
}

public class B
{
     A a;
     public B() { this.a =  new A(this); }
}

如果我有像上面这样设计的类,这些类的对象会被垃圾收集器(GC)收集吗?

假设我这样做:

void f()
{
     B b = new B();
}

在这个方法中,我创建了一个名为 bB 实例,当方法返回时,b 超出范围,GC 应该能够收集它,但如果要收集它,它必须先收集 a,它是 B 的成员,要收集 a,它需要先收集 b,它是 A 的成员。它变成圆形。所以我的问题是:这样的循环引用会阻止 GC 收集对象吗?

如果是,那么我们如何避免这个问题呢?我们如何确保在我们的类设计中没有循环引用?是否有任何工具(或编译器选项)可以帮助我们检测循环引用?

如果不是,我们在哪里以及为什么使用 WeakReference 类?它的目的是什么?

至少从逻辑上讲,其中一个引用总是很弱:在您的示例中,cleary A 不能依赖于 B,因为必须首先创建 A,作为 B 的成员,因此 A } 只能持有对 B 的弱引用。因此,先销毁 B 然后再销毁 A 是安全的。递归论证,每个计算机程序都必须如此。永远不可能有一个真正的、完全对称的循环依赖。
@KerrekSB:我同意这个逻辑。但这是从创造的角度来看的。 GC会不会分析这个(即这些类的每个方法中的代码,毕竟只有这样才能得出那个结论)?我的意思是,如果您在创建对象后看到它们,那么它看起来很有问题。
why do we use WeakReference class? What is its purpose? 我用它——例如——缓存数据(在字典中)。如果它还活着,很好,如果没有,我会在第一次请求时再次加载它。
@KerrekSB 我真的不明白你的意思。无论何时创建它们都没有关系,如果它们相互使用来执行任何操作,它们就会相互依赖。 A处的内存指向B,B处的内存指向A。这是循环引用吗?在任何一种状态下,你要么在 A 处,要么在 B 处。你是第一个还是第二个并不重要。除非您指的是一些我不知道的数学概念,否则我认为将其称为循环参考是完全正确的。

G
Guru Stron

.NET 垃圾收集器绝对可以处理循环引用。垃圾收集器如何工作的非常高级的视图是......

从局部变量、静态变量和 GC 固定对象开始。这些都不能收集

标记通过遍历这些对象的子对象可以到达的每个对象

收集所有未标记的对象。

这允许很好地收集循环引用。只要它们中的任何一个都无法从已知无法收集的对象中到达,那么循环引用本质上是无关紧要的。

注意:我意识到我遗漏了许多有趣的细节,以使这个答案简单直接


那不是计算成本高吗
h
hunter

不,这不是问题,因为 GC 可以处理循环引用

MSDN 说

如果一组对象包含相互引用,但这些对象都没有直接或间接地从堆栈或共享变量中引用,那么垃圾回收将自动回收内存。


m
mfeingold

几个答案已经解释了循环引用不是问题。

至于弱引用 - 使用它们的原因是缓存。

当 GC 遍历对象依赖树时,他会忽略弱引用。换句话说,如果对一个对象的唯一引用是弱引用,它将被垃圾收集,但如果在创建引用和您尝试使用之间没有垃圾收集,您仍然可以访问该对象。


d
driis

不,循环引用不会影响垃圾收集器,它将完全能够收集 B 的实例。

垃圾收集器知道超出范围后没有人可以引用B的实例,因此没有人可以使用B的实例间接引用A。