我正在编写一些测试,以更好地理解.NET垃圾收集器的工作方式,以便构建没有内存泄漏的框架。但是在我的第一个非常简单的测试中,我面临着意外的行为。
这是我对GC的了解:
- 它会定期(并在可能的时候)清理事物
- 它清除不再引用的实例
这是我为验证我的知识而编写的小班课程:
public class People
{
private People _child;
private WeakReference<People> _parent = new WeakReference<People>(null);
public void AddChild(People child)
{
_child = child;
_child._parent.SetTarget(this);
}
}
基本上,父母可以引用其子女。根据我的上述知识,我希望当父母“死亡”时,其子女也是如此。
这里的小技巧是使用WeakReference
,以便孩子可以访问其父对象,但无需创建可能导致内存泄漏的循环引用(这是我试图找出的重点之一) :是否有两个实例仅互相引用垃圾收集?或者换句话说:在这种情况下我是否必须使用WeakReference
?我猜是如果它们直接互相引用也不会被垃圾收集,但是我实际上从未检查过它。)
这是我用xUnit
写的小测试:
public class GCTests
{
[Fact]
public void TestGC()
{
var parent = new People();
var weakParent = new WeakReference(parent);
var child = new WeakReference(new People());
parent.AddChild(child.Target as People);
// Until now,there is a reference to the parent so the GC is not supposed to collect anything
parent = null;
// But now,no-one is referencing the parent so I expect the GC to collect it and removed it from memory
// Forces the GC to collect unreferenced instances
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Assert.False(weakParent.IsAlive);
Assert.False(child.IsAlive);
}
}
在Assert.False(weakParent.IsAlive)
上测试失败,这意味着仍然有人引用了实际的父母。
我还尝试使用Thread.Sleep(10000);
给GC时间来收集东西,但是在该断言上仍然失败。
所以我的问题是:为什么我的实例没有被垃圾收集?
- 我的哪一个断言是错误的?
- 在垃圾回收过程中或在使用
WeakReference
时我会误解什么?
有关信息,我正在使用目标.NET Core 3
的xUnit测试项目,但我希望它不会改变GC流程。