6

试图找出 OutOfMemoryException 的底部,我发现 WCF 的缓冲 TransferMode 使用的 .net 的BufferManagers负责浪费数百兆字节(请参阅问题和我自己的关于如何在我的 WCF 客户端中防止 BufferManager / PooledBufferManager的答案应用程序浪费内存?有关详细信息以及如何通过简单地从“缓冲”切换到“流式”来修复它)。

撇开 WCF 不谈,BufferManagers 被发明为比您通常所做的更好的替代方案:在需要时简单地分配字节数组,并依靠 GC 清理它们并在引用超出范围时回收它们。

所以我的问题是:有没有人在现实世界的应用程序中使用过 BufferManagers,以便在性能方面产生显着差异,以证明必须手动 .Clear() BufferManager(如果有必要的话)带来的不便?

如果是这样,是否可以仅手动创建一个单字节缓冲区并保留对它的引用并不能解决该特定问题?

4

1 回答 1

3

我最近致力于接受多个客户端连接(最多 500 个同时连接)的代理服务。代理将客户端请求中继到目标服务器,并将目标服务器的响应中继回客户端。代理服务使用字节数组 (Byte[]) 作为缓冲区来发送和接收数据。我没有缓冲区管理器。

代理每次都创建一个新的字节数组以从套接字发送和接收数据。资源监视器中的私有字节不断增加。运行 ANT Memory Profiler 工具显示不断增加的大片段。

解决方案是实现一个简单的 Buffermanager 类来管理 Buffer 使用的内存。这是代码片段

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);
            }
        }


    }
于 2012-10-25T13:51:46.910 回答