试图找出 OutOfMemoryException 的根源,我发现 .net 的 BufferManagers由 WCF 的缓冲 TransferMode 使用,实际上浪费了数百兆字节(有关详细信息以及如何通过简单地从“缓冲”切换到“流”来修复它,请参阅 How can I prevent BufferManager / PooledBufferManager in my WCF client app from wasting memory? 上的问题和我自己的答案)。
抛开 WCF 不谈,BufferManager 的发明是作为一种比您通常所做的更好的替代方案:在需要时简单地分配字节数组,并在引用超出范围时依靠 GC 来清理和回收它们。
所以我的问题是:是否有人在现实世界的应用程序中使用过 BufferManager,从而在性能方面产生了显着的差异,以证明必须手动 .Clear() BufferManager(如果有必要)带来的不便?
如果是这样,是否可以手动创建一个单字节缓冲区并保留对其的引用并不能解决该特定问题?
最佳答案
我最近开发了一个代理服务,该服务接受多个客户端连接(最多 500 个同时连接)。代理将客户端请求中继到目标服务器,并将目标服务器的响应中继回客户端。代理服务使用字节数组 (Byte[]) 作为发送和接收数据的缓冲区。我没有适当的缓冲区管理器。
代理每次都会创建一个新的字节数组以从套接字发送和接收数据。资源监视器中的私有(private)字节不断增加。运行 ANT Memory Profiler 工具显示出不断增加的大碎片。
解决方案是实现一个简单的 Buffermanager 类来管理缓冲区使用的内存。这是代码片段
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);
}
}
}
关于.net - BufferManager 的真实用例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7265299/