вторник, 8 октября 2013 г.

Setting WCF MaxBufferPoolSize quota can cause memory leak

If you use WCF and TransferMode.Buffered be careful with MaxBufferPoolSize setting. If non-zero value is used, then buffer manager won't release allocated memory. Which means if you set high value, let's say Int32.MaxValue, and you send or receive large message then this memory won't be reclaimed by GC.

Using 0 as MaxBufferPoolSize switches to GCBufferManager which simply allocates and releases memory every time buffer requested.

For more details look at System.ServiceModel.Channels.BufferManager internals.

2 комментария:

  1. Generally speaking the error you could possible received will be

    System.InsufficientMemoryException. Failed to allocate a managed memory buffer of X bytes
    For example: X could be 536870912.
    OR
    Exception of type 'System.OutOfMemoryException' was thrown.
    at System.IO.MemoryStream.set_Capacity(Int32 value)
    at System.IO.MemoryStream.EnsureCapacity(Int32 value)
    at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
    at WcfExtensions.GZipMessageEncoder.DecompressBuffer(ArraySegment`1 buffer, BufferManager bufferManager)
    at WcfExtensions.GZipMessageEncoder.ReadMessage(ArraySegment`1 buffer, BufferManager bufferManager, String contentType)
    at System.ServiceModel.Channels.HttpInput.DecodeBufferedMessage(ArraySegment`1 buffer, Stream inputStream)
    at System.ServiceModel.Channels.HttpInput.ReadBufferedMessage(Stream inputStream)
    at System.ServiceModel.Channels.HttpInput.ParseIncomingMessage(HttpRequestMessage httpRequestMessage, Exception& requestException)
    at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
    at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
    at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
    at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
    at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
    at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)

    That's means you have the memory leak!!!

    In my case it happens in GZipMessageEncoder class
    The quick fix was change this method

    public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType)
    {
    //bufferManager = BufferManager.CreateBufferManager(8547483647, 2147483647);
    bufferManager = BufferManager.CreateBufferManager(0, 2147483647);
    //Decompress the buffer
    var decompressedBuffer = DecompressBuffer(buffer, bufferManager);
    //Use the inner encoder to decode the decompressed buffer
    var returnMessage = innerEncoder.ReadMessage(decompressedBuffer, bufferManager);
    returnMessage.Properties.Encoder = this;
    return returnMessage;
    }

    However, setting the BufferManager to GCBufferManager will cause a overload in the CPU Process and decrease in the general performance in the application.

    I strongly recommend to read this two post and try other solutions.
    http://stackoverflow.com/questions/4043683/wcf-httptransport-streamed-vs-buffered-transfermode/6896088#6896088
    http://stackoverflow.com/questions/7252417/how-can-i-prevent-buffermanager-pooledbuffermanager-in-my-wcf-client-app-from/7253103#7253103

    ОтветитьУдалить
  2. "...However, setting the BufferManager to GCBufferManager will cause a overload in the CPU Process and decrease in the general performance in the application..."
    I don't think GCBufferManager is _always_ a bad thing, it may degrade performance if you process 10K RPS. But no matter which BufferManager you use, you shouldn't create it in per message basis:

    public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType)
    {
    //bufferManager = BufferManager.CreateBufferManager(8547483647, 2147483647);
    bufferManager = BufferManager.CreateBufferManager(0, 2147483647);

    ОтветитьУдалить

Wider Two Column Modification courtesy of The Blogger Guide