给出以下代码:
namespace GcDemo
{
class Program
{
static void Main(string[] args)
{
var list = new List<object>();
Console.WriteLine("list is in {0} generation.", GC.GetGeneration(list));
GC.Collect();
Console.WriteLine("list is in {0} generation.", GC.GetGeneration(list));
GC.Collect();
list.Add(new object());
Console.WriteLine("list is in {0} generation. object is in {1} generation.", GC.GetGeneration(list), GC.GetGeneration(list[0]));
GC.Collect(0);
Console.WriteLine("list is in {0} generation. object is in {1} generation.", GC.GetGeneration(list), GC.GetGeneration(list[0]));
}
}
}
列表对象在Gen 2中,而它是gen 0中唯一的引用列表[0]对象。为什么它知道不在GC.Collect(0)中收集它?
答案 0 :(得分:2)
This article简单地写了它的工作原理;阅读“让世代与写障碍一起工作”部分。 This是另一个很好的博客文章,更详细地解释了技术。
执行摘要是CLR发出的代码,因此它可以检测Gen2对象的写入时间。它将写入记录到进行Gen0收集时检查的数据结构(“卡表”)中;这允许它找到Gen2 - &gt; Gen0引用没有在内存中遍历所有对象的全部成本。
答案 1 :(得分:1)
我是对的,你的问题是,为什么GC没有收集list[0]
?
如果是这样,那么答案是,因为list
内部有对你创建的对象的引用(因为List<T>
内部有一个数组T[]
来保存项目。)
所以你的参考链看起来像这样:Program.Main()
- &gt; list
- &gt; list[0]
,因此GC无法(也不应该)收集此对象,无论您的对象在哪一代。
编辑:你开始收集gen 0的地方,... list[0]
在gen 0 +引用(见上文)=&gt; GC 不收集list[0]
,但将其移至第1代。
答案 2 :(得分:1)
主要规则非常简单,当没有任何引用时,会收集一个对象。您添加到列表中的对象无法收集,列表中有对它的引用。列表无法收集,您的代码有参考。一直到最后一个声明。每次强制收集时,都会强制对象移动到下一代。
唯一可能令人困惑的是垃圾收集器可以看到列表存储了对象的引用。它肯定会。必然如此,你不希望对象随机消失。 CLR所做的很大一部分是为垃圾收集器提供了尽快发现它所需的信息。
可能更令人困惑的是,它还可以看到您的代码具有对列表的引用。这是抖动的重要组成部分,它构造了一个表,说明代码的哪一部分引用了局部变量。由于最后一个语句引用了列表,因此您无法在代码段中看到它。