c# - 如何防止 WCF 客户端应用程序中的 BufferManager/PooledBufferManager 浪费内存?

标签 c# .net wcf buffer-manager

分析一个 WCF 客户端应用程序(我没有写,仍然不太了解),它通过 SOAP 与一堆服务对话,运行几天后会抛出 OutOfMemoryException,我发现 .net 的 PooledBufferManager 会永远不要释放未使用的缓冲区,即使应用程序内存不足,导致 OOME。

这当然符合规范:http://msdn.microsoft.com/en-us/library/ms405814.aspx

The pool and its buffers are [...] destroyed when the buffer pool is reclaimed by garbage collection.



请随意回答以下问题中的一个,因为我有很多问题,有些是更一般的问题,有些是针对我们的应用程序对 BufferManager 的使用。

首先是关于(默认池化)BufferManager 的几个一般性问题:

1) 在我们有 GC 的环境中,为什么我们需要一个 BufferManager 来保留未使用的内存,即使这会导致 OOME?我知道,有 BufferManager.Clear(),您可以使用它来手动清除所有缓冲区 - 如果您有权访问 BufferManager,那就是。进一步查看为什么我似乎没有访问权限。

2) 尽管 MS 声称“这个过程比每次需要使用缓冲区时创建和销毁缓冲区要快得多。”,他们不应该把它留给 GC(例如它的 LOH)并优化 GC ?

3) 在执行 BufferManager.Take(33 * 1024 * 1024) 时,我将获得 64M 的缓冲区,因为 PooledBufferManager 将缓存该缓冲区以供以后重用,这可能 - 好吧,在我的情况下不是,因此它纯粹是浪费内存 - 比如说,需要 34M、50M 或 64M。那么创建这样一个潜在的非常浪费的 BufferManager 是否明智,它被 HttpsChannelFactory 使用(默认情况下,我假设)?我没有看到内存分配的性能应该如何重要,特别是当我们谈论 WCF 和应用程序将每 10 秒 TOPS 进行一次通信的网络服务时,通常要多几秒甚至几分钟。

现在有一些与我们的应用程序使用 BufferManager 相关的更具体的问题。该应用程序连接到几个不同的 WCF 服务。对于它们中的每一个,我们为 http 连接维护一个连接池,因为连接可能同时发生。

检查一个堆转储中的单个最大对象,一个 64M 字节的数组,在我们的应用程序初始化时只使用过一次,之后不需要,因为服务的响应只有在初始化时才那么大,顺便说一句。对于我使用过的许多应用程序来说都是典型的,即使这可能会受到优化(缓存到磁盘等)。 WinDbg 中的 GC 根分析产生以下结果(我将专有类的名称清理为“MyServiceX”等):
0:000:x86> !gcroot -nostacks 193e1000
DOMAIN(00B8CCD0):HANDLE(Pinned):4d1330:Root:0e5b9c50(System.Object[])->
035064f0(MyServiceManager)->
0382191c(MyHttpConnectionPool`1[[MyServiceX, MyLib]])->
03821988(System.Collections.Generic.Queue`1[[MyServiceX, MyLib]])->
038219a8(System.Object[])->
039c05b4(System.Runtime.Remoting.Proxies.__TransparentProxy)->
039c0578(System.ServiceModel.Channels.ServiceChannelProxy)->
039c0494(System.ServiceModel.Channels.ServiceChannel)->
039bee30(System.ServiceModel.Channels.ServiceChannelFactory+ServiceChannelFactoryOverRequest)->
039beea4(System.ServiceModel.Channels.HttpsChannelFactory)->
039bf2c0(System.ServiceModel.Channels.BufferManager+PooledBufferManager)->
039c02f4(System.Object[])->
039bff24(System.ServiceModel.Channels.BufferManager+PooledBufferManager+BufferPool)->
039bff44(System.ServiceModel.SynchronizedPool`1[[System.Byte[], mscorlib]])->
039bffa0(System.ServiceModel.SynchronizedPool`1+GlobalPool[[System.Byte[], mscorlib]])->
039bffb0(System.Collections.Generic.Stack`1[[System.Byte[], mscorlib]])->
12bda2bc(System.Byte[][])->
193e1000(System.Byte[])

查看由 BufferManager 管理的其他字节数组的 gc 根显示其他服务(不是“MyServiceX”)具有不同的 BufferPool 实例,因此每个服务都在浪费自己的内存,他们甚至没有共享浪费。

4) 我们在这里做错了吗?我无论如何都不是 WCF 专家,所以我们可以让各种 HttpsChannelFactory 实例都使用相同的 BufferManager 吗?

5) 或者甚至更好,我们可以告诉所有 HttpsChannelFactory 实例根本不要使用 BufferManagers 并要求 GC 做它该死的工作,即“管理内存”?

6) 如果问题 4) 和 5) 无法回答,我是否可以访问所有 HttpsChannelFactory 实例的 BufferManager 并对其手动调用 .Clear() - 这远非最佳解决方案,但它已经在我的这样的话,一个服务实例不仅可以释放上述64M,还可以释放64M + 32M + 16M + 8M + 4M + 2M!因此,仅此一项就可以使我的应用程序持续更长时间而不会遇到内存问题(不,除了 BufferManager 之外,我们没有内存泄漏问题,尽管我们确实消耗了大量内存并在整个过程中积累了大量数据很多天,但这不是这里的问题)

最佳答案

我相信我已经回答了你的问题 #5:

5) Or maybe even better, could we just tell all HttpsChannelFactory instances NOT to use BufferManagers at all and ask the GC to do its god-damn job, which is 'managing memory'?



有一个 MaxBufferPoolSize 绑定(bind)参数,它控制 BufferManager 中缓冲区的最大大小。将其设置为 0 将禁用缓冲,并且 GCBufferManager 将被创建而不是池化 - 一旦消息被处理,它将 GC 分配的缓冲区,如您的问题。

本文讨论WCF memory buffer management更详细。

关于c# - 如何防止 WCF 客户端应用程序中的 BufferManager/PooledBufferManager 浪费内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7252417/

相关文章:

c# - Ninject 与 WCF 双工 channel

c# - 签署外发消息

wcf - 试图让 WCF 客户端使用 wss 1.0 用户名 token 安全

c# - 如何从C#查询VB6 IDE的模式

c# - 为什么Electron杀死带有冒号参数的Windows进程?

c# - UWP:如何降低所选图像的质量?

c# - 如何使用 NAudio C# 更改播放速度

c# - 将我的逻辑放在事件处理程序中还是放在 MVVM 的 setter 中更好(Xamarin `Picker` `SelectedItem` 怪癖)

c# - 错误: New transaction is not allowed because there are other threads running in the session in c#

.net - 使用应用程序配置在 .NET 4 中运行 .NET 3.5 构建的混合模式程序集也需要 Framework 3.5