Search code examples
c#unmanagedidisposablereference-type

IDisposable, unmanaged fields, reference types and assignments


If I have two instances of a class that implement IDisposable what happens to the unmanaged fields within the first class if it is assigned to the second.

For example, consider the following simplified class:

public unsafe class Image : IDisposable
{
    private float* pixelsBase;

    private GCHandle pixelsHandle;

    public Image(int width, int height)
    {
        this.Width = width;
        this.Height = height;

        // Assign the pointer and pixels.
        this.Pixels = new float[width * height * 4];
        this.pixelsHandle = GCHandle.Alloc(this.Pixels, GCHandleType.Pinned);
        this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
    }

    public float[] Pixels {get; private set;}

    ~ImageBase()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {

        if (disposing)
        {
            // Dispose of any managed resources here.
        }

        if (this.pixelsHandle.IsAllocated)
        {
            this.pixelsHandle.Free();
            this.pixelsBase = null;
        }
    }
}

What would happen to the pixelsBase field in an instance of the Image class was assigned to another one?

e.g

var firstImage = new Image(100, 200);
var secondImage; new Image(300, 300);

firstImage = secondImage;

Solution

  • IDisposable has nothing to do with your question, since you're never calling it. The only thing that matters is the finalizer - while IDisposable and finalizers are related, the runtime doesn't care about IDisposable at all.

    So, what do you get from using a finalizer? When your object is no longer referenced, its finalizer is put on the finalizer queue, and unless something process-killing happens, is executed after a while. In your case, this will release the GCHandle and allow the Pixels byte array to be collected as well.

    Don't try to write C++ in C#, or understand C# in terms of C++. They look similar, but are very different - especially in memory management aspects. C# finalizers have very little in common with C++ destructors.

    As a side-note, you want to avoid pinning managed objects for long - it prevents heap compaction from working properly, which means that unless your Pixels array is on the LOH, you're not going to get any memory below any pinned handle reclaimed when a collection occurs (disclaimer: this is an implementation detail of the current MS.NET runtime; contractually, .NET doesn't even need a garbage collector at all, and finalizers aren't guaranteed to ever run, much less finish).

    Allocating unmanaged memory for your array might be a better idea if you're already dealing with pointers anyway. If you really want to use a managed array for your backing store, using fixed in a scope where you need the unmanaged pointer might be a better area than keeping the whole thing pinned for the lifetime of the whole object. In fact, your solution needs at least two collections to release the object's memory - the first one to eventually call the GCHandle.Free, the second to actually reclaim the memory that is now no longer pinned. Even if you manually call Dispose, you still need to wait for a collection to actually reclaim the memory.