+1.916.577.1977 | Downloads | Buy | Register | Login
 Search  
Friday, July 25, 2008
Search Blogs
 

Available Blogs
 

Previous Blogs
 

Technorati
 
More blogs about coversant.

About Coversant
 

Object Pools
 
Location: BlogsMullin' with Mullins    
Posted by: Chris Mullins 4/7/2007

There seem to be two primary use cases for Object Pools that we keep running across while building the SoapBox Platform:

  1. Expensive Objects. Many object are inherently expensive, and we need to make sure we can avoid the penalties associated with startup and destruction. Objects that fall into this category are LDAP Connections and Active Directory Connections. Database connections would also count, but they're managed by the ADO.Net Connection Pool.
  2. Memory Management. Handling memory efficiently in a server application is tricky. This is especially true if large chunks of memory are frequently handed off to Windows for use as buffers. When memory is handed to Windows it is pinned, which means that it can't be moved around by the Garbage Collector. This ends up creating holes all over the heap, and leads to memory exhaustion. There are a number of good articles on the problem that are floating around the net:

To address both of these use cases, we've gone through a few different iterations of building a Buffer Pool. Ideally, a good buffer pool has the following characteristics:

  1. Easily specialized for any type of Object.
  2. Grows and shrinks in big chunks, in a way that is memory friendly.
  3. Minimizes holes in the Garbage Collected Heap.
  4. Is not frequency scanned for Garbage Collection. This means it should live in Gen2 or in the Large Object Heap.
  5. Is thread aware, and can be used from multiple threads.

To be sure, there are a few other minor requirements, but those are the big ones.

The solution presented here is a segmented algorithm. The overall design of the class is easily summarized:

  1. An object pool has 1 or more Segments in it.
  2. A segment has a configurable number of object in it.
  3. When an object is 'Checked Out' of the pool, it's checked out from the oldest segment possible.
  4. New Segments are created when there are no available object for check out.
  5. A timer periodically scans the "newest" segment. If that segment has all objects checked in, and hasn't been used for a while, then the segment is torn down (subject to configurable limits for a Minimum Segment Count).

To create a concrete pool of object from the ObjectPool, you need to derive a concrete class, call the initialize method on the base class, and provide an implementation of GetObjectInstance. For example, here is a Byte Buffer pool. This class creates byte arrays that are handed off to Windows Sockets (for BeginRead, and BeginSend) in order to prevent excessive pinning.

public class BufferPool : ObjectPoolBase<Byte[]>
{
    private readonly int _size;

    public BufferPool(int itemsPerSegment, int minSegments)
        : base()
    {
        _size = 4096;
        Initialize(itemsPerSegment, minSegments, true, 1000 * 60 * 5);

    }

    public BufferPool(int itemsPerSegment, int minSegments, int size)
        : base()
    {
        _size = size;
        Initialize(itemsPerSegment, minSegments, true, 1000 * 60 * 5);
    }

    protected override byte[] GetObjectInstance()
    {
        return new byte[_size];
    }
}

This code calls Initialize and tells the ObjectPool to perform a Full Garbage Collection prior to creating instances for the segments (this is to force a heap compaction, to be sure our segments are creates as close to the bottom of the heap as possible) and to check once every 5 minutes for old, unused segments that are eligible for cleanup.

Users of the Object Pool will be checking objects out of the pool, and using the standard .Net Dispose pattern to check objects back in. This pattern is the same as ADO.Net, and should be familiar to most developers. To accomplish this, we wrap all object in a Wrapper that provides Check-In-On-Dispose, as well as a Finalizer to catch things that get forgotten. To check a Byte Array out of the pool:

WrappedObject<Byte[]> byteBuffer = myPool.CheckOut();

To check the object back into the Pool:

byteBuffer.Dispose();

You can, of course, use the standard C# or VB.Net Using keyword to let .Net take care of calling Dispose for you.

To get to the actual instance of the object being wrapped, you'll need to use the Instance property on the WrappedObject class.

One of the neat features of this pool, is the use of Object Resurrection in case a developer forgets to call dispose on a checked out Wrapper. To read more about Resurrection you can check out:

Object Resurrection in this pool is done by the WrappedObject class. By implementing a Finalizer, the class is able to make sure it eventually gets back into the pool should a user forget.

~WrappedObject()
{
    // If the AppDomain is being unloaded, or the CLR is 
    // shutting down, just exit gracefully
    if (Environment.HasShutdownStarted)
        return;

    //Object Reserrection in Action!
    GC.ReRegisterForFinalize(this);

    // return this instance back to the owning queue.
    _owningObjectPool.CheckIn(_owningSegment, _instance);
}

The finalizer won't typically be run, as when the user Disposes the WrappedObject instance the Dispose method tells the GC not to call the Finalizer. It's only if the user forgets that the finalizer will be hit.

Overall, I'm happy with the algorithm used by this pool. The basic tenant used internally by the pool, 'Always check out the oldest available object' means that when we hit a burst situation, the Object Pool grows to accommodate the burst by creating new segments, but once the burst settles itself out, those segments can be reclaimed over time.

public void Dispose()
{
    if (_disposed)
        return;

    _disposed = true;
    _owningObjectPool.CheckIn(_owningSegment, _instance);
    GC.SuppressFinalize(this);
}

For anyone looking for more information on Dispose and Finalization patterns, see Dispose, Finalization, and Resource Management by Joe Duffy

Now, before I give you the link to the source code, a few disclaimers are needed:

  1. We are NOT using this pool in production today, so it may have bugs! Our server currently uses an older version of the pool, which doesn't have the cleanup capabilities or the segmentation architecture this pool has. There are plans to integrate it 'Some Day', but as of now, it's a low priority.
  2. This is code you're finding on the Internet, on some random guy's blog. While I think the quality is pretty good, there may be some bugs. Use it at your own risk.
  3. If you do find any bugs, please post them here, so I can fix any problems.
  4. There is still certainly room for improvement, please feel free to make suggestions.

You can find the code at: Object Pools



Technorati Tags: , , , ,

My Technorati Profile
Permalink |  Trackback

©2008 Coversant, Inc. | Privacy Policy | About Coversant | Contact Info