flow.
"Flow is a condition of deep, nearly meditative involvement." - Tom DeMarco

How to Use the Singleton Pattern and the Garbage Collector to Manage an Expensive Resource

Wednesday, May 09, 2007 3:29 PM
Ever have a read only, expensive resource that everyone seems to want?  Sure you have.  Here's an easy way to optimize that access as well as give the garbage collector a hand, too.

First, lets take a look at our class which we'll be using to control our resource.  ImageWrapper is a singleton which controls access to a single Bitmap file, named Sunset.jpg.  Many people want to use Sunset.jpg but no one needs to edit it, in other words, all access is read only.  This is a key point to remember for this example, this only works for read only scenarios.  Now, let's take a look at ImageWrapper...

    public class ImageWrapper : IDisposable
    {
        private static ImageWrapper _imageWrapper = new ImageWrapper();
        private Image _image;
        private static int _referenceCount;
        private int _sizeOfImage;
 
        private ImageWrapper()
        {
            // Create our image
            string filePath = @"C:\Sunset.jpg";
            _image = new Bitmap(filePath);
 
            // The image is unmanaged, better let the garbage collector know
            // that it's there
            _sizeOfImage = File.ReadAllBytes(filePath).Length;
            GC.AddMemoryPressure(_sizeOfImage);
        }
 
        ~ImageWrapper()
        {
            Dispose(false);
        }
 
        public static ImageWrapper Instance
        {
            get
            {
                // One more reference to the image...
                _referenceCount++;
 
                return _imageWrapper;
            }
        }
 
        public void Dispose()
        {
            // One less reference to the image...
            _referenceCount--;
 
            Dispose(true);
        }
 
        protected virtual void Dispose(bool calledFromDisposeMethod)
        {
            if (calledFromDisposeMethod)
            {
                // Is anyone else holding onto the image?  If not, let's clean
                // it up
                if (_referenceCount == 0)
                {
                    DestroyImageResource();
                    GC.SuppressFinalize(this);
                }
            }
            else
            {
                // We were called from the Finalizer, this means that no one
                // else is holding onto the Image so its safe to clean it up
                DestroyImageResource();
            }
        }
 
        private void DestroyImageResource()
        {
            // Clean up our image
            _image.Dispose();
 
            // Let the garbage collector know that its gone
            GC.RemoveMemoryPressure(_sizeOfImage);
        }
    }


Wow, that's a lot of code.  In fact, it looks like there's a lot going on here but as we break it down we'll see that it's actually not that bad.  First, lets look at the structure.  This class implements the Singleton pattern.  Now a full discussion of the Singleton pattern is beyond the scope of this article, but in short, the Singleton assures us that there is ever only one instance of a given object at a time.  The Singleton pattern is very cool in the sense that it's a very effective pattern with a clear purpose that's very easy to grok.  In fact, this is often why it's most people's gateway pattern (not to be confused with the Gateway Pattern).  It's also for this reason that the Singleton is probably the most overused pattern ever.  Everyone uses the Singleton pattern for everything and always qualifies it with 'but this is a really good use of the Singleton pattern'.  Now, you can find more about the Singleton pattern here but for now just trust me...this is a really good use of the Singleton pattern

Once we understand the Singleton pattern, let's take a look at how we're actually creating our one and only instance of the object in our private constructor.

        private ImageWrapper()
        {
            // Create our image
            string filePath = @"C:\Sunset.jpg";
            _image = new Bitmap(filePath);
 
            // The image is unmanaged, better let the garbage collector know
            // that it's there
            _sizeOfImage = File.ReadAllBytes(filePath).Length;
            GC.AddMemoryPressure(_sizeOfImage);
        }


Note that we're finding out the size of the file.  Once we know it, we use the GC.AddMemoryPressure(long) call to inform the garbage collector that additional data has been placed in memory.  You see, bitmaps are unmanaged resources (Image is more orless a wrapper for the underlying GDI+ image manipulation classes) and the garbage collector only knows about managed resources.  Therefore, we could easily create an Image class around a 500 megabyte file and the garbage collector would never be the wiser...it would only ever know about the reference to the Image class on the stack which could seriously skew its perceptions of the amount of memory available.  AddMemoryPressure(long) solves this problem by giving a hint to the collector which allows it to schedule its collections a little more efficiently.

Next let's take a look at our Instance property and our Dispose() method.

        public static ImageWrapper Instance
        {
            get
            {
                // One more reference to the image...
                _referenceCount++;
 
                return _imageWrapper;
            }
        }
 
        public void Dispose()
        {
            // One less reference to the image...
            _referenceCount--;
 
            Dispose(true);
        }


You've probably noticed that both of these members contain a reference to a member variable called _referenceCount.  _referenceCount is a static integer that we increment every time someone accesses the ImageWrapper through the Instance property.  Then each time they Dispose() of it, we decrement it.  Why?  This helps us keep track of how many people are currently using the underlying image, once our reference count goes to 0 we can safely remove the image because we can be sure that no one else is using it.  If you've ever worked with classic COM and dealt with its reference counting system then this should feel familiar to you.

What happens if someone forgets to call Dispose()?  Excellent question!  Keen observers will have noticed that this class implements IDisposable as well as implements the Dispose pattern.  Like the Singleton, the Dispose pattern is a bit out of the scope for this article but curious minds can read up on it here.  The gist of the IDisposable interface is that it gives you an efficient way to quickly dispose of resources soon after consumers have finished with them.  The gist of the Dispose pattern is that it gives you a way to ensure that your resource will still be disposed of even if your users forget to call Dispose()*. 

That brings us to the final key of the ImageWrapper class.

        protected virtual void Dispose(bool calledFromDisposeMethod)
        {
            if (calledFromDisposeMethod)
            {
                // Is anyone else holding onto the image?  If not, let's clean
                // it up
                if (_referenceCount == 0)
                {
                    DestroyImageResource();
                    GC.SuppressFinalize(this);
                }
            }
            else
            {
                // We were called from the Finalizer, this means that no one
                // else is holding onto the Image so its safe to clean it up
                DestroyImageResource();
            }
        }
 
        private void DestroyImageResource()
        {
            // Clean up our image
            _image.Dispose();
 
            // Let the garbage collector know that its gone
            GC.RemoveMemoryPressure(_sizeOfImage);
        }


This overload of Dispose() is what actually takes care of cleaning up our resource.  Note that we check _referenceCount each time this method is called, once it gets to zero we know that no one else is using our resource and we can safely dispose of it.  This keeps us from accidentally destroying the image while someone is still using it.  If someone forgets to call Dispose() then no problem, we'll still clean up when this Dispose() is called from the Finalizer (represented in C# by the thing that looks suspiciously like a destructor).  Note that in DestroyImageResource() after we dispose of the image we call GC.RemoveMemoryPressure(long).  This informs the garbage collector that pressure we placed on the memory in the beginning has now been lifted so that it may relax its collections accordingly.

Here's how we would use the ImageWrapper class in practice...

    class Program
    {
        static void Main(string[] args)
        {
            // Create some instances of the ImageWrapper
            ImageWrapper imageWrapper01 = ImageWrapper.Instance;
            ImageWrapper imageWrapper02 = ImageWrapper.Instance;
            ImageWrapper imageWrapper03 = ImageWrapper.Instance;
 
            // Use the ImageWrapper
 
            // Clean up the ImageWrapper
            imageWrapper01.Dispose();
            imageWrapper02.Dispose();
            imageWrapper03.Dispose();
        }
    }


All of these tiny little tricks are handy enough by themselves, but when used together they can really maximize the use of your resources when it matters the most.

*Well, almost ensure...see CriticalFinalizers for more details.

kick it on DotNetKicks.com

Feedback

# re: How to Use the Singleton Pattern and the Garbage Collector to Manage an Expensive Resource

Nice article. Couple of questions, however.

First, is File.ReadAllBytes(filePath) the best way to find the actual size of the file??? You're essentially opening the file twice--once to load it into the image and once to count how many bytes. If someone was to use this class for serious lifting they'd definitely see a performance issue. A better way would be to get the size of the file on disk. You'd be a max 4kb off (cluster size on NTFS), but you'd cut your load time in half.

Second, all access is read only by the consent of the callers only. If this object were exposed through an API it might be a good idea to also extend the Image object to prevent write access.

Again, nice article. Saved. 5/10/2007 8:32 AM | mcgurk

# re: How to Use the Singleton Pattern and the Garbage Collector to Manage an Expensive Resource

Thanks mcgurk!

That's a great point. File.ReadAllBytes() is definitely not the most efficient way to get the size, FileInfo.Length would probably perform much efficiently.

Also, I totally agree. "Read Only", in this case, is definitely at the discretion of the reader...if this was production code we would definitely have to take some additional steps to ensure true read only access.

Thanks for the comment, those are great points!
Jeremy 5/10/2007 8:45 AM | Jeremy

 re: How to Use the Singleton Pattern and the Garbage Collector to Manage an Expensive Resource

You should probably point out that this is not thread-safe. 5/14/2007 7:45 AM | Mike

 re: How to Use the Singleton Pattern and the Garbage Collector to Manage an Expensive Resource

This seems quite poor code to me. As Mike says, it is not thread-safe (your reference count won't work properly), but that is only the beginning of its problems. As a consumer of this code, I have no way to know if the instance has been disposed or not, and there is no mechanism to re-create the instance once it has been disposed. Also, it breaks the contract that implementing IDisposable implies, i.e. that the resource will be cleaned up if I call the Dispose() method. If I happened to call the Instance get method twice instead of once before calling Disposed, it would not be cleaned up, but if I only called the property once it would be. Bizarre and unpredictable behaviour is not a good trait in a design. Instead of trying to count references, hack around with the dispose pattern, and give the GC hints, why not just use a WeakReference to allow the GC to reclaim memory when necessary? 5/14/2007 9:57 AM | Matt Howells

# re: How to Use the Singleton Pattern and the Garbage Collector to Manage an Expensive Resource

A great overview of singletons with respect to threading can be found at http://www.yoda.arachsys.com/csharp/singleton.html 5/14/2007 12:32 PM | Billy McCafferty

# re: How to Use the Singleton Pattern and the Garbage Collector to Manage an Expensive Resource

Thanks guys, you're entirely correct. That code is not thread safe at all. I actually had intended to mention that (and ironically provide that same link ) but somehow that little 'detail' must have gotten lost in the shuffle :)

Thanks for adding that link hopefully, that should clear things up. 5/17/2007 4:57 PM | Jeremy

# re: How to Use the Singleton Pattern and the Garbage Collector to Manage an Expensive Resource

Mike,

Thanks for you comment. You brought up some very valid points. I'm actually planning on featuring weak references in a future post, though I hadn't considered using them in this situation. They may indeed be a much lower maintenance and more elegant way to do this. That's definitely some food for thought!

Jeremy 5/17/2007 4:59 PM | Jeremy

# Phentermine no prescription.

Cheap phentermine no prescription. Phentermine prescription. Phentermine no prescription. Phentermine 37.5 no prescription needed. Purchase online phentermine 37.5mg no prescription. 9/4/2007 9:16 PM | Phentermine prescription online.

Post a comment





 

Please add 2 and 3 and type the answer here: