Why the garbage collector does not garbage my instances?
我正在编写一些测试以更好地了解 .NET 垃圾收集器的工作原理,以便构建一个没有内存泄漏的框架。但是在我的第一个非常简单的测试中,我遇到了意外的行为。
这是我对 GC 的了解:
- 它会定期清理东西(以及何时清理)
- 它清理不再引用的实例
这是我为验证我的知识而写的小类:
1 2 3 4 5 6 7 8 9 10 11 | 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); } } |
基本上,父母可以引用它的孩子。根据我上面的知识,我希望当父母"死"时,它的孩子也会"死"。
这里的小技巧是使用
这是我用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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); } } |
测试在
我也尝试使用
所以我的问题是:为什么我的实例没有被垃圾回收?
- 我的哪一个断言是错误的?
-
我在垃圾回收过程中或在
WeakReference 的使用中误会了什么?
作为参考,我正在使用的 xUnit 测试项目的目标是
在这个问题的核心,你似乎想知道 GC 是否可以收集唯一引用是循环的对象。我想我可以通过创建两个仅相互引用的非常大的对象来测试这一点,然后让它们超出范围并创建第三个大对象,看看我是否可以引入一些内存压力来尝试让 GC免费的东西了。如果 GC 可以释放相互引用的对象,那么程序消耗的内存应该会在某个时候减少。如果 GC 不能,那么内存使用只会增加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | using System.Threading; namespace ConsoleApp { class Program { static void Main() { Thread.Sleep(2000); SetupBigThings(); Thread.Sleep(2000); string big = new string('a', 1000000000); while (true) { Thread.Sleep(2000); } } static void SetupBigThings() { Thread.Sleep(1000); BigThing x = new BigThing('x'); Thread.Sleep(1000); BigThing y = new BigThing('y') { OtherBigThing = x }; x.OtherBigThing = y; Thread.Sleep(1000); } } class BigThing { public BigThing OtherBigThing { get; set; } private string big; public BigThing(char c) { big = new string(c, 750000000); } } } |
查看代码,我们应该会在 3 秒时看到内存峰值,然后在 4 秒时再次出现...... 5 秒后,大对象超出范围,也许在 7 秒左右,当下一个大对象时,它们将被 GC对象已创建
这几乎就是图表所显示的内容:
因此,我假设 GC 确实可以收集彼此唯一引用的对象。简单地说"哪些对象有 0 个引用?"可能不会那么天真,而是会追逐引用路径,并且任何仅引用另一个已经考虑进行 GC 的节点的对象也被认为是 GC'able。虽然我不是 GC 内部工作的专家