I have a WCF client application which makes a single service call with a very large response (1GB). I'm finding making this service call uses a lot of memory (500MB) which seems to never get reclaimed even though the response objects are no longer referenced by my code.
I've used a memory profiler to see much of the memory usage lies in byte arrays created by PooledBufferManager instances.
The proxy/client I am using has been auto generated by Visual Studio, so it is a class derived from System.ServiceModel.ClientBase< TChannel >.
I am using a custom binding with the following configuration:
<customBinding>
<binding name="foo"
closeTimeout="00:01:00"
openTimeout="00:01:00"
receiveTimeout="00:01:00"
sendTimeout="00:01:00">
<transactionFlow/>
<reliableSession ordered="true" inactivityTimeout="00:02:00"/>
<security authenticationMode="SecureConversation" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<secureConversationBootstrap messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<localClientSettings maxClockSkew="23:59:00"/>
</secureConversationBootstrap>
<localClientSettings maxClockSkew="23:59:00"/>
</security>
<mtomMessageEncoding>
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
</mtomMessageEncoding>
<httpTransport maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"/>
</binding>
</customBinding>
Reading around people talk about a few solutions to this:
- switching to streaming response mode instead of buffered, but I believe this is for TCP WCF connections, not for HTTP like mine.
- setting maxBufferPoolSize to zero to disable buffer pools. Setting this in the httpTransport element doesn't seem to have any effect for me.
- calling Clear() on the appropriate BufferManager/PooledBufferManager. I can't find the object to call this on from the context of the ClientBase derived instance I have. I did manage to find the appropriate PooledBufferManager instance using the debugger and digging many levels deep into private innerChannelFactory fields, but this isn't useful to interact with it from code.
- manually invoking GC.Collect() seems to reclaim about 50MB of the outstanding 500MB.
What would be the best way to reclaim as much of this memory as possible used by this one time service call? I'm on the verge of making the service call in a dedicated process I can kill to reclaim the memory at this point.