Here's an interesting use of SciTech's .NET Memory Profiler – watching how objects are handled by the .NET CLR garbage collector.

The sample code create 4 objects – a simple class "Simple", a class that implements IDisposable "Disposable", and a class that also implements a destructor "Destructable".

.NET Memory Profiler can generate a real-time graph of memory allocations. I've instrumented the sample code using SciTech's API to add comments to the graph so you can match the code execution path against the X axis (time). The object instance count is mapped to the Y axis.

image

See how 2 instances of the "Destructable" class existed around the 10 second mark – then one was released at the first GC, then the 2nd (which has the destructor) is only released after the 2nd GC.

using System; using System.Diagnostics; using SciTech.NetMemProfiler;

namespace MemoryTesting { internal class Program { private static void Main(string[] args) { MemProfiler.FullSnapShot("Start");

        CreateSimple();

        CreateDisposable();
        System.Threading.Thread.Sleep(1000);

        CreateDestructableAndDispose();
        System.Threading.Thread.Sleep(1000);

        CreateDestructable();
        System.Threading.Thread.Sleep(1000);

        MemProfiler.AddRealTimeComment("GC");
        GC.Collect();

        System.Threading.Thread.Sleep(2000);
        MemProfiler.AddRealTimeComment("WaitForPendingFinalizers");
        GC.WaitForPendingFinalizers();

        System.Threading.Thread.Sleep(2000);

        MemProfiler.AddRealTimeComment("GC");
        GC.Collect();

        System.Threading.Thread.Sleep(1000);

        MemProfiler.AddRealTimeComment("End");
        MemProfiler.FullSnapShot();

    }

    private static void CreateSimple()
    {
        MemProfiler.AddRealTimeComment("Create Simple");

        var a = new Simple();
        a.Something += OnEventHandler;
        a.Data = "hey";

        a.Something -= OnEventHandler;

    }

    private static void CreateDisposable()
    {
        MemProfiler.AddRealTimeComment("Create Disposable");

        var a = new Disposable();
        a.Something += OnEventHandler;
        a.Data = "ho";
        System.Threading.Thread.Sleep(1000);
        a.Something -= OnEventHandler;

        a.Dispose();
    }

    private static void CreateDestructableAndDispose()
    {
        MemProfiler.AddRealTimeComment("Create Destructable and Dispose");

        var a = new Destructable();
        a.Something += OnEventHandler;
        a.Data = "haha";
        System.Threading.Thread.Sleep(1000);
        a.Something -= OnEventHandler;

        a.Dispose();
    }

    private static void CreateDestructable()
    {
        MemProfiler.AddRealTimeComment("Create Destructable");

        var a = new Destructable();
        a.Something += OnEventHandler;
        a.Data = "haha";
        System.Threading.Thread.Sleep(1000);
        a.Something -= OnEventHandler;
        //a.Dispose();
    }

    private static void OnEventHandler(object sender, EventArgs e)
    {
        Debug.WriteLine("Something Fired");
    }
}

public class Simple
{
    private string \_data;

    public string Data
    {
        get { return \_data; }
        set
        {
            \_data = value;
            OnSomething(null);
        }
    }

    public event EventHandler Something;

    protected virtual void OnSomething(EventArgs e)
    {
        EventHandler handler = Something;
        if (handler != null) handler(this, e);
    }
}

public class Disposable : Simple, IDisposable
{
    #region IDisposable Members

    public void Dispose()
    {
        System.Threading.Thread.Sleep(1000);

        MemProfiler.AddRealTimeComment("Dispose");

        Dispose(true);
        GC.SuppressFinalize(this);
    }

    #endregion

    protected virtual void Dispose(bool disposing)
    {
    }
}

public class Destructable : Disposable
{
    ~Destructable()
    {
        System.Threading.Thread.Sleep(1000);

        MemProfiler.AddRealTimeComment("Destructing");

        Dispose(false);
    }
} }