Wednesday, 1 March 2017

IDisposable

The IDisposable interface defines a dispose method, this method is implemented to give your class a chance to release managed & unmanaged resources. Take a look at this implementation of our person class.

class Person {
    byte[] bytes = new byte[50000];
    public string Name { get; set; }

    public Person(string Name) {
        this.Name = Name;
        Console.WriteLine($"Initializeing {Name}");
    }
}

Very simple notice how we allocate an array of 50000 bytes for each instance of our Person class, this array is a managed resource, garbage collection will take it out eventually. now let's take a look at this in action.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("memory in use");
        Console.WriteLine(GC.GetTotalMemory(false));
           
        Console.ReadKey();
        var p1 = new Person("Pawel");
        Console.WriteLine(GC.GetTotalMemory(false));
        Console.ReadKey();

        var p2 = new Person("Marin");
        Console.WriteLine(GC.GetTotalMemory(false));  
        Console.ReadKey();

        var p3 = new Person("Magda");
        Console.WriteLine(GC.GetTotalMemory(false));
        Console.ReadKey();

        var p4 = new Person("Tomek");
        Console.WriteLine(GC.GetTotalMemory(false));
        Console.ReadKey();
          
        p1 = null;
        Console.WriteLine("Dereference Pawel");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        p2 = null;
        Console.WriteLine("Dereference Marin");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        p3 = null;
        Console.WriteLine("Dereference Magda");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        p4 = null;
        Console.WriteLine("Dereference Tomek");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();
    }

}

Very simple application, as we create instances of our Person class we display an approximation of our memory usage, and more or less we see it increase by about the size of our byte array. After we create our instances of the person object, we start to dereference it; basically set the variable to null, which breaks the reference between the stack and heap allowing the garbage collector to clean up our memory, and thus decreasing our apps memory usage by again more less the size of our byte array.

let's modify our Person class

class Person
{
    public Person(string Name)
    {
        this.Name = Name;
        Console.WriteLine($"Initializeing {Name}");
    }
    bool living = true;
    public string Name { get; set; }
    byte[] bytes = new byte[50000];
    public async void BackgroundTask()
    {
        while (living)
        {
            await Task.Delay(5000);
            Console.WriteLine($"{Name}" + GC.GetTotalMemory(true));
        }
    }

}

what we did was added a "living" bool and initialized it to true, then we created background task that would loop while our "living" variable is true and just output our persons name and the total memory our application is using. Now let's call our BackgorundTask method from our main

class Program
{
    static void Main(string[] args)
    {
        //create pawel
        var p1 = new Person("Pawel");
        p1.BackgroundTask();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        //dereference pawel
        p1 = null;
        Console.WriteLine("Dereference Pawel");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        //create marin
        var p2 = new Person("Marin");
        p2.BackgroundTask();
        Console.WriteLine(GC.GetTotalMemory(false));
        Console.ReadKey();

        //dereference marin
        p2 = null;
        Console.WriteLine("Dereference Marin");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        //create magda
        var p3 = new Person("Magda");
        p3.BackgroundTask();
        Console.WriteLine(GC.GetTotalMemory(false));
        Console.ReadKey();

        //derefrence magda
        p3 = null;
        Console.WriteLine("Dereference Magda");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        //create tomek
        var p4 = new Person("Tomek");
        p4.BackgroundTask();
        Console.WriteLine(GC.GetTotalMemory(false));
        Console.ReadKey();

        //dereference tomek
        p4 = null;
        Console.WriteLine("Dereference Tomek");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        Console.WriteLine("done");
        Console.ReadKey();
    }

}

now when we run our application we alternate between creating and dereferencing our Person object, however we start our backgroundTask for each initialization of our Person class on initialization. You'll notice that after we set our variables p1, p2, p3 and p4 to null and force the garbage collector to run, however our memory usage doesn't drop, and our background task still prints to the console. This is because the background task method is preventing the garbage collector from collecting the dereferenced instances of our Person class.

what our Person class needs is some sort of way to deactivate the background task so that the garbage collector can collect our dereferenced instances of the "Person" class. In comes the IDisposable interface, this interface forces us to create a dispose method who's job is to clean up our class for garbage collection.

class Person : IDisposable
{
    public Person(string Name)
    {
        this.Name = Name;
        Console.WriteLine($"Initializeing {Name}");
    }
    bool living = true;
    public string Name { get; set; }
    byte[] bytes = new byte[50000];
    public async void BackgroundTask()
    {
        while (living)
        {
            await Task.Delay(5000);
            Console.WriteLine($"{Name}" + GC.GetTotalMemory(true));
        }
        Console.WriteLine($"{Name} Background task complete");
    }

    public void Dispose()
    {
        Console.WriteLine($"Disposing {Name}");
        living = false;
    }

}

Now our implementation of the dispose function sets our living field to false, and when our background task method fires it simply doesn't enter the loop and goes out of scope, then when we set the corresponding person variable (p1,p2,p3,p4) to null our garbage collector can clean it up.

class Program
{
    static void Main(string[] args)
    {
        //create pawel
        var p1 = new Person("Pawel");
        p1.BackgroundTask();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        //dereference pawel
        p1.Dispose();
        p1 = null;
        Console.WriteLine("Dereference Pawel");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        //create marin
        var p2 = new Person("Marin");
        p2.BackgroundTask();
        Console.WriteLine(GC.GetTotalMemory(false));
        Console.ReadKey();

        //dereference marin
        p2.Dispose();
        p2 = null;
        Console.WriteLine("Dereference Marin");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        //create magda
        var p3 = new Person("Magda");
        p3.BackgroundTask();
        Console.WriteLine(GC.GetTotalMemory(false));
        Console.ReadKey();

        //derefrence magda
        p3.Dispose();
        p3 = null;
        Console.WriteLine("Dereference Magda");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        //create tomek
        var p4 = new Person("Tomek");
        p4.BackgroundTask();
        Console.WriteLine(GC.GetTotalMemory(false));
        Console.ReadKey();

        //dereference tomek
        p4.Dispose();
        p4 = null;
        Console.WriteLine("Dereference Tomek");
        GC.Collect();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();

        Console.WriteLine("done");
        Console.ReadKey();
        Console.WriteLine(GC.GetTotalMemory(true));
        Console.ReadKey();
    }

}

now one thing that we can do to clean our code up a little is to leverage a using statement, a using statement bock calls the dispose method of an object.

class Program
{
    static void Main(string[] args)
    {
        using (var p1 = new Person("Pawel"))
        {
            p1.BackgroundTask();
            Console.WriteLine($"Memory Usage: {GC.GetTotalMemory(true)}");
            Console.ReadKey();
        }

        Console.WriteLine("done");
        Console.ReadKey();
    }

}

the above will call the dispose method of p1 once the program flow exists the using block.