Given the following code:
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]));
}
}
}
The list object is in Gen 2 while it is the only reference list[0] object that is in gen 0. How come it knows not to collect it in GC.Collect(0) ?
This article has a simple writeup of how it works; read the “Making Generations Work with Write Barriers” section. This is another good blog post explaining the techinque in more detail.
The executive summary is the CLR emits code so it can detect when the Gen2 object was written to. It records the write into a data structure (the “card table”) that it inspects when doing the Gen0 collection; this allows it to find the Gen2 -> Gen0 reference without the full cost of walking all objects in memory.