.NET Memory - Heap Generations

Contents

Introduction

I last posted .NET Memory Management 101 where I talked about what value types were, what reference types were and showed that we can see them with a memory profiler like dotMemory.
This time we’ll look a bit more at the heap, specifically at generations.

Generations are used by the garbage collector to partition objects by how long it thinks they should live. It will then scan for objects to clean up more frequently in the sections that to thinks contains short lived objects.

Microsoft have a good introduction in their Fundamentals of Garbage Collection article.

We’re going to see if we can see in action again.

You can get the code for this example from my Git repository.

What are Generations?

According to the specifications there are three generations.

  1. Generation 0.
  2. Generation 1.
  3. Generation 2.

There’s the Large Object Heap. This is, as you can guess, for storing large objects; specifically, objects greater than 85Kb.

When an object is created it is put in generation 0. Generation 0 is for the shortest lived objects and gets the largest portion of the garbage collector’s attention. Objects are the promoted from generation 0 to generation 1 and generation 2 as the garbage collector determines it appropriate.

The question now is “How does the garbage collector decide when to promote an object?”. That bit is actually rather simple. If the garbage collector scans the object and finds that it is not ready for collection then it will promote it up to the next generation. It’s that simple.

Seeing it in action

What we’re going to do is quite simple. We’re just going to create object and watch it move up the generations as we call GC.Collect().

We have this very simple method that prints out the generation the object is in and the instructs the garbage collector to collect.

private static void GcCollect(object o)
{
    Console.WriteLine($"Object is in generation {GC.GetGeneration(o)}.");
    GC.Collect();
}

All we’re going to do is call this four times. We should see the object advance a generation until it gets to two and then stay there.

The full code is:

static void Main()
{
    var o = new object();

    var maxGeneration = GC.MaxGeneration;

    Console.WriteLine($"This environment has {maxGeneration} generations.");

    for (var i = 0; i < maxGeneration + 2; i++)
    {
        GcCollect(o);
    }
    Console.WriteLine("Finished...");
    Console.Read();
}

private static void GcCollect(object o)
{
    Console.WriteLine($"Object is in generation {GC.GetGeneration(o)}.");
    GC.Collect();
}

Currently, I think all the frameworks have 3 generations of memory. I might be wrong there, but there’s no harm in doing it right, even if it is a demo.

When we run the code we see the output we expect.

This environment has 2 generations.
Object is in generation 0.
Object is in generation 1.
Object is in generation 2.
Object is in generation 2.
Finished...

The object stayed in memory because I was using it in each iteration of the GcCollect(object) method. Once I am no longer using it to print out the message to the console the garbage collector will note that it there aren’t any references left and with collect it.

Weak References

We have a catch-22 here.

  1. We want to know if the object is garbage collected.
  2. To do this we need a reference to the object.
  3. If I have a reference to an object the garbage collector won’t collect it.
  4. GOTO 1

There is a solution though in the form of the WeakReference class.

A weak reference is a reference that the garbage collector should ignore when deciding whether an object is eligible for collection. We can use it here to hold a reference without preventing collection. We create a weak reference like so:

var weakReference = new WeakReference(o);

So, we’ll create one, run the GC. Then we’ll set o to be null so that there are no ‘strong’ references left and the GC should see it as collectable.

We’ll add the following to the previous code:

 var weak = new WeakReference(o);
 GC.Collect();
 Console.WriteLine($"Has o has been garbage collected? {(weak.IsAlive? "No" : "Yes")}");

The output confirm that the object is no longer alive.

Has o has been garbage collected? Yes

If we had other strong references to the object then the GC would not have collected the object.

Thanks for reading.

Feel free to contact me @BanksySan!

No comments:

Post a Comment