Trying to get to the bottom of an OutOfMemoryException I found that .net's BufferManagers, used by WCF's buffered TransferMode, were responsible for wasting literally hundreds of megabytes (see the question and my own answer on How can I prevent BufferManager / PooledBufferManager in my WCF client app from wasting memory? for details and how I could fix it by simply switching from 'buffered' to 'streamed').
Leaving aside WCF, BufferManagers were invented as a better performing alternative to what you would normally do: simply allocating byte arrays when you need them and relying on the GC to clean them up and recycle once the reference goes out of scope.
So my question is: Has anyone used BufferManagers in a real-world app so that it made a noticeable difference in terms of performance to justify the inconvenience of having to manually .Clear() the BufferManager (if that was necessary)?
And if so, could just manually creating a single byte buffer and keeping a reference to it not have solved that particular problem?
I recently worked on a Proxy service which accepted multiple client connections (upto 500 simultaneous connections). The Proxy relayed client requests to the destination server and relayed back responses from destination servers to the clients. The proxy service used Byte arrays (Byte[]) as buffers to send and receive data. I had no Buffer Manager in place.
The proxy was creating a new byte array every time to send and receive data from socket. The private bytes in the resource monitor kept increasing. Running ANT Memory Profiler tool showed large fragments which kept increasing.
The solution was to implement a simple Buffermanager class to manage the memory used by the Buffers. Here is the code snippet
public class BufferManager
{
private readonly int m_ByteSize;
private readonly Stack<byte[]> m_Buffers;
private readonly object m_LockObject = new Object();
#region constructors
public BufferManager(int _byteSize, int _poolCount)
{
lock (m_LockObject)
{
m_ByteSize = _byteSize;
m_Buffers = new Stack<Byte[]>(_poolCount);
for (int i = 0; i < _poolCount; i++)
{
CreateNewSegment();
}
}
}
#endregion //constructors
public int AvailableBuffers
{
get { return m_Buffers.Count; }
}
public System.Int64 TotalBufferSizeInBytes
{
get { return m_Buffers.Count * m_ByteSize; }
}
public System.Int64 TotalBufferSizeInKBs
{
get { return (m_Buffers.Count * m_ByteSize/1000); }
}
public System.Int64 TotalBufferSizeInMBs
{
get { return (m_Buffers.Count * m_ByteSize/1000000); }
}
private void CreateNewSegment()
{
byte[] bytes = new byte[m_ByteSize];
m_Buffers.Push(bytes);
}
/// <summary>
/// Checks out a buffer from the manager
/// </summary>
public Byte[] CheckOut()
{
lock (m_LockObject)
{
if (m_Buffers.Count == 0)
{
CreateNewSegment();
}
return m_Buffers.Pop();
}
}
/// <summary>
/// Returns a buffer to the control of the manager
/// </summary>
///<remarks>
/// It is the client’s responsibility to return the buffer to the manger by
/// calling Checkin on the buffer
///</remarks>
public void CheckIn(Byte[] _Buffer)
{
lock (m_LockObject)
{
m_Buffers.Push(_Buffer);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With