Click here to Skip to main content
Click here to Skip to main content

C# Object Pooling

, 12 Oct 2007
Rate this:
Please Sign up or sign in to vote.
A simple article on object pooling for the brave

Introduction

Before I get blasted, let me mention Stoyan Damov's article on object pooling. Although my method was developed independently, it does use several of the same techniques.

This code makes use of the WeakReference object to perform object pooling. The assumption is made that if the .NET runtime attempts to reclaim memory before the object is needed again, it is cheaper to reclaim the memory. This assumption is only valid under the assumption I made when designing this class, i.e. this type of object pooling works well for applications that require large number of expensive objects, fast. In fact, the only resource these objects occupy is large sections of memory, of which consecutive sections are expensive to allocate. While I cannot really see an application for this in business settings, primarily because so much of ASP.NET is object creation happy, I can foresee many applications in the scientific realm, especially graphics processing and AI. Although I have not been able to use this code in a professional setting, timings indicate a significant speed improvement over just creating objects over and over.

I am not going to spend a lot of time itemizing the code in this article. Instead it will be included as an attachment. Please read the basic algorithm first (below) and then examine the source. I think this particular project will be more fun to probe and pick apart without direct guidance.

Using the Code

The basic algorithm of the ObjectPool class is as follows:

  • Request a new object via generic method ObjectPool.GetInstance().GetObjectFromPool(null);
  • Loop through the linked list to find an unused object
  • Pass parameters to the object's SetupObject method
  • Return reference
  • On dispose, add to linked list
//#define DISABLE_POOL
using System;
using System.Collections.Generic;
using System.Text;

namespace ObjectPool {
    public interface IObjectPoolMethods : IDisposable {
        /// <span class="code-SummaryComment"><summary>
</span>
        /// This method is used to setup an instance of
        /// an object.
        /// <span class="code-SummaryComment"></summary>
</span>
        /// <span class="code-SummaryComment"><remarks>
</span>
        /// Use an empty constructor in the class and
        /// entirely rely on this method to setup and
        /// initialize the object if the class is going
        /// to be used for object pooling. Do not call this
        /// method from the constructor or it will be called
        /// twice and will affect performance.
        /// <span class="code-SummaryComment"></remarks>
</span>
        /// <span class="code-SummaryComment"><param name="setupParameters"></param>
</span>
        void SetupObject(params object[] setupParameters);
        /// <span class="code-SummaryComment"><summary>
</span>
        /// This event must be fired when dispose is called
        /// or the Object Pool will not be able to work
        /// <span class="code-SummaryComment"></summary>
</span>
        event EventHandler Disposing;
    }

    /// <span class="code-SummaryComment"><summary>
</span>
    /// A generic class that provide object pooling functionality
    /// to classes that implement the IObjectPoolMethods interface
    /// <span class="code-SummaryComment"></summary>
</span>
    /// <span class="code-SummaryComment"><remarks>
</span>
    /// As long as the lock(this) statements remain this class is thread
    /// <span class="code-SummaryComment"></remarks>
</span>
    /// <span class="code-SummaryComment"><typeparam name="T">
</span>
    /// The type of the object that should be pooled
    /// <span class="code-SummaryComment"></typeparam>
</span>
    public class ObjectPool<T> where T : IObjectPoolMethods, new() {
        /// <span class="code-SummaryComment"><summary>
</span>
        /// The MAX_POOL does not restrict the maximum
        /// number of objects in the object pool
        /// instead it restricts the number of
        /// disposed objects that the pool will maintain
        /// a reference too and attempt to pool. This
        /// number should be set based on a memory and
        /// usage anaylsis based on the types of objects
        /// being pooled. I have found the best results
        /// by setting this number to average peak objects
        /// in use at one time.
        /// <span class="code-SummaryComment"></summary>
</span>
        /// <span class="code-SummaryComment"><value>100</value>
</span>
        private const int MAX_POOL = 2000;
        /// <span class="code-SummaryComment"><summary>
</span>
        /// The static instance of the object pool. Thankfully the
        /// use of generics eliminates the need for a hash for each
        /// different pool type
        /// <span class="code-SummaryComment"></summary>
</span>
        private static ObjectPool<T> me = new ObjectPool<T>();
        /// <span class="code-SummaryComment"><summary>
</span>
        /// Using a member for the max pool count allows different
        /// types to have a different value based on usage analysis
        /// <span class="code-SummaryComment"></summary>
</span>
        private int mMaxPool = ObjectPool<T>.MAX_POOL;
        /// <span class="code-SummaryComment"><summary>
</span>
        /// A Linked List of the WeakReferences to the objects in the pool
        /// When the count of the list goes beyond the max pool count
        /// items are removed from the end of the list. The objects at the
        /// end of the list are also most likely to have already 
        /// been collected by the garbage collector.
        /// <span class="code-SummaryComment"></summary>
</span>
        private LinkedList<WeakReference> objectPool = 
                new LinkedList<WeakReference>();
        /// <span class="code-SummaryComment"><summary>
</span>
        /// Return the singleton instance
        /// <span class="code-SummaryComment"></summary>
</span>
        /// <span class="code-SummaryComment"><returns>
</span>
        /// The single instance allowed for the given type
        /// <span class="code-SummaryComment"></returns>
</span>
        public static ObjectPool<T> GetInstance() {
            return me;
        }
        /// <span class="code-SummaryComment"><summary>
</span>
        /// This method gets called on the object disposing
        /// event for objects created from this object pool class.
        /// If the implementing class does not fire this event on
        /// dispose the object pooling will not work
        /// <span class="code-SummaryComment"></summary>
</span>
        /// <span class="code-SummaryComment"><param name="sender">
</span>
        /// The class instance that is pooled
        /// <span class="code-SummaryComment"></param>
</span>
        /// <span class="code-SummaryComment"><param name="e">
</span>
        /// An empty event args parameters
        /// <span class="code-SummaryComment"></param>
</span>
        /// <span class="code-SummaryComment"><exception cref="System.ArgumentException">
</span>
        /// Thrown if the type of the object in the weak reference 
        /// does not match the type of this generic instance
        /// <span class="code-SummaryComment"></exception>
</span>
        private void Object_Disposing(object sender, EventArgs e) {
            //The lock is required here because this method may
            //be called from events on different threads
            lock (this) {
                Add(new WeakReference(sender));
            }
        }
        /// <span class="code-SummaryComment"><summary>
</span>
        /// This method will add a new instance to be tracked by
        /// this object pooling class. This should be a reference
        /// to an object that was just disposed otherwise it makes no
        /// sense and will likely cause some serious problems.
        /// <span class="code-SummaryComment"></summary>
</span>
        /// <span class="code-SummaryComment"><param name="weak">
</span>
        /// WeakReference to the object that was disposed
        /// <span class="code-SummaryComment"></param>
</span>
        /// <span class="code-SummaryComment"><exception cref="System.ArgumentException">
</span>
        /// Thrown if the type of the object in the weak reference 
        /// does not match the type of this generic instance
        /// <span class="code-SummaryComment"></exception>
</span>
        private void Add(WeakReference weak) {
            objectPool.AddFirst(weak);
            if (objectPool.Count >

The linked list is of a fixed maximum size in this code, however, with the immediate dispose method used by the testing application provided, I rarely see a total number of objects created that exceed the number of threads used. If the runtime has disposed an item in the list, the entire list is truncated since the order of the list is done by last time used. While this isn't actually correct because of the nature of the .NET Garbage Collector, it is effectively correct.

Something that I have noticed with this algorithm/code is that as the memory allocated decreases, the difference between Object Pool and non-object pool goes away (ok, duh) but as the number of objects required in a given unit of time increases, without the pooling, the slower the application runs as the OS begins to thrash. The dispose method does not seem to be called.

Conclusion

Like I said in my brief introduction above, I haven't found a good application for this yet. I might try plugging it into my memory graph function solver algorithm (see my Bridges article for a simple implementation of the solver), but until then it was just a fun proof of concept. I would be interested in comments describing how similar algorithms have been used in a high object count scenario (as opposed to Connection Pools which have low object counts but are resource heavy).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Ennis Ray Lynch, Jr.
Architect ERL GLOBAL, INC
United States United States
My company is ERL GLOBAL, INC. I develop Custom Programming solutions for business of all sizes. I also do Android Programming as I find it a refreshing break from the MS.

Comments and Discussions

 
GeneralMax number of objects instanciated Pinmemberargrithmag21-Feb-08 8:43 
GeneralRe: Max number of objects instanciated PinmemberEnnis Ray Lynch, Jr.21-Feb-08 8:50 
GeneralPooling and Transaction management in .NET Pinmembercmsreddy5-Dec-07 3:28 
GeneralRe: Pooling and Transaction management in .NET PinmemberEnnis Ray Lynch, Jr.14-Dec-07 4:16 
QuestionApplications? PinmemberDewey12-Oct-07 18:09 
AnswerI was just referring to applications PinmemberEnnis Ray Lynch, Jr.12-Oct-07 19:07 
For this type of pooling the benefit is the faster access and object creation. The problem is I can't think of an application because the only resource is massive amounts of objects with massive amounts of memory. Whereas other pooling is for low numbers of objects occupying expsenive resources. In fact, as you mentioned with database connections, you almost don't have a choice in pooling. .NET is setup by default to pool connection strings that are identical. Also, .NET provides the Queue Worker item for thread pooling. But these are still low numbers.
 
Now if you can come up with an application that could benefit from this sort of pooling let me know. I am interested.
 

 

Need a C# Consultant? I'm available.


Happiness in intelligent people is the rarest thing I know. -- Ernest Hemingway

GeneralRe: I was just referring to applications PinmemberMike Doyon16-Oct-07 4:38 
GeneralReferring to your sig PinmemberEnnis Ray Lynch, Jr.16-Oct-07 6:04 
GeneralYour Signature! PinmemberYouMiss18-Nov-07 15:09 
GeneralRe: Your Signature! PinmemberMike Doyon18-Nov-07 15:16 
GeneralRe: I was just referring to applications Pinmembereverweb18-Nov-08 12:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141220.1 | Last Updated 12 Oct 2007
Article Copyright 2007 by Ennis Ray Lynch, Jr.
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid