3

我有一个 WCF 客户端应用程序,它发出一个具有非常大响应 (1GB) 的服务调用。我发现进行此服务调用会使用大量内存(500MB),即使我的代码不再引用响应对象,这些内存似乎也永远不会被回收。

我使用内存分析器查看了大部分内存使用情况是由 PooledBufferManager 实例创建的字节数组。

我使用的代理/客户端是由 Visual Studio 自动生成的,所以它是一个派生自 System.ServiceModel.ClientBase<TChannel> 的类。

我正在使用具有以下配置的自定义绑定:

  <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>

阅读周围的人谈论一些解决方案:

  • 切换到流式响应模式而不是缓冲,但我相信这是用于 TCP WCF 连接,而不是像我这样的 HTTP。
  • 将 maxBufferPoolSize 设置为零以禁用缓冲池。在 httpTransport 元素中设置它似乎对我没有任何影响。
  • 在适当的 BufferManager/PooledBufferManager 上调用 Clear()。我无法从我拥有的 ClientBase 派生实例的上下文中找到要调用它的对象。我确实设法使用调试器找到了合适的 PooledBufferManager 实例,并深入挖掘了许多级别到私有 innerChannelFactory 字段,但这对于从代码中与之交互没有用处。
  • 手动调用 GC.Collect() 似乎回收了剩余的 500MB 中的大约 50MB。

尽可能多地回收此一次性服务调用所使用的内存的最佳方法是什么?我即将在一个专用进程中进行服务调用,此时我可以杀死以回收内存。

4

1 回答 1

2
  • 切换到流式响应模式而不是缓冲,但我相信这是用于 TCP WCF 连接,而不是像我这样的 HTTP。

即使使用 http,您实际上也可以像我对https所做的那样切换到流式传输模式,但http也可以正常工作

只需为您的元素添加一个transferMode="Streamed"属性。httpTransport由于您关心客户,因此您需要在客户中执行此操作app.config。(您也可以在服务器的 web.config 中独立完成,如果您还想将服务器更改为streamed模式。但无需更改客户端和服务器,传输模式不会更改线路上的字节)

  • 将 maxBufferPoolSize 设置为零以禁用缓冲池。在 httpTransport 元素中设置它似乎对我没有任何影响。

这正是本文声称应该起作用的:

如果 maxBufferPoolSize = 0,则创建一个 GCBufferManager,否则你将获得一个 PooledBufferManager。前者是微不足道的,实际上并没有真正做任何管理,只是为任何请求分配一个新的缓冲区,并让垃圾收集器负责处理它。

GC.Collect()在这种情况下,手册实际上可以解决问题。

  • 在适当的 BufferManager/PooledBufferManager 上调用 Clear()。我无法从我拥有的 ClientBase 派生实例的上下文中找到要调用它的对象。

当我尝试访问时,我也无法访问该 BufferManager 实例。

  • 手动调用 GC.Collect() 似乎回收了剩余的 500MB 中的大约 50MB。

不适用于池化缓冲区管理器,因为它一旦创建就永远不会释放缓冲区,除非您调用Clear()缺少指向实例的指针,但您不能这样做。

于 2014-04-08T05:22:32.767 回答