Using the BufferManager

A BufferManager recycles the byte buffers used when reading and writing buffered messages. There's some allocation overhead creating these frequently used buffers, making buffer recycling a net win in high-throughput scenarios. As you move to larger message sizes though, buffer recycling becomes less of a factor and then eventually a net loss. All of this is encoded in the BufferManager class so that you don't have to think about it.

When you create a BufferManager, you specify the size of the heap to draw from. Once the total concurrent allocations exceed this heap size, the buffer manager begins returning non-recyclable buffers. The second tunable parameter of a BufferManager is the maximum size of a recyclable buffer. Again, if an allocation exceeds this maximum size, then the buffer manager simply returns a non-recyclable buffer. This limit prevents the buffer manager from holding blocks of memory for large messages, which tends to be inefficient.

 public abstract class BufferManager
{
   protected BufferManager();

   public abstract void Clear();
   public static BufferManager CreateBufferManager(long maxBufferPoolSize, int maxBufferSize);
   public abstract void ReturnBuffer(byte[] buffer);
   public abstract byte[] TakeBuffer(int bufferSize);
}

The basic functionality of a buffer manager is very similar to a heap with explicit malloc and free. You grab a buffer using TakeBuffer and later give it back using ReturnBuffer. You cannot return someone else's memory using ReturnBuffer. The most common source of this is if you allocate your own byte array or pull from another buffer manager, and then give that memory to a message encoder. This will fail after the encoder is done with the buffer. Here's what that failure exception tends to look like:

 Unhandled Exception: System.ArgumentException: This buffer can not be returned to the buffer manager because it is the wrong size.
Parameter name: buffer
   at System.ServiceModel.Channels.BufferManager.PooledBufferManager.ReturnBuffer(Byte[] buffer)
   at System.ServiceModel.Channels.BufferedMessageData.DoClose()
   at System.ServiceModel.Channels.BufferedMessageData.Close()
   at System.ServiceModel.Channels.BufferedMessage.OnClose()
   at System.ServiceModel.Channels.Message.Close()
   at System.ServiceModel.Channels.Message.System.IDisposable.Dispose()

The final method in the BufferManager class is Clear, which allows you to flush the cache. You might call Clear when your server goes idle after handling enough messages to have touched a lot of buffers, although I haven't seen a frequent need for applications to do this.

Next time: Introducing IDefaultCommunicationTimeouts