分析一个WCF客户端应用程序(我没有写,但仍然不知道太多),通过SOAP与一堆服务对话,运行几天后将抛出一个OutOfMemoryException,我发现.net的即使应用程序内存不足,PooledBufferManager也永远不会释放未使用的缓冲区,从而导致OOME。如何防止WCF客户端应用程序中的BufferManager/PooledBufferManager浪费内存?
这当然符合规格为:http://msdn.microsoft.com/en-us/library/ms405814.aspx
游泳池和缓冲区[...]当缓冲池 由垃圾回收回收销毁。
请随时回答下面的问题,因为我有一堆问题,一些更一般的性质,以及一些特定于我们的应用程序使用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秒钟通过一次,通常是更多秒甚至更长的时间。
现在就我们的应用程序使用BufferManager的一些更具体的问题。该应用程序连接到几个不同的WCF服务。对于它们中的每一个,我们都为http连接维护一个连接池,因为连接可能会同时发生。
检查一个堆转储中一个最大的对象,一个64M字节的数组,它在初始化时只在应用程序中使用过一次,之后不再需要,因为服务的响应只在初始化时大,这btw。对于我已经使用的许多应用程序来说是典型的,尽管这可能会受到opimization(缓存到磁盘等)的影响。在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[])
在GC根用于由BufferManager管理的其他字节数组展望显示,其他服务(而不是'MyServiceX')有不同的BufferPool实例,所以每个人都在浪费自己的内存,他们甚至不共享这些浪费。
4)我们在这里做错了什么?我不是一个WCF专家,所以我们可以让各种HttpsChannelFactory实例都使用同一个BufferManager?
5)或者甚至更好,可能我们只是告诉所有HttpsChannelFactory情况下,不是在所有使用BufferManagers并要求GC尽自己的神该死的工作,这就是“管理存储”?
6)如果问题4)和5)不能回答,我能获得所有HttpsChannelFactory实例的BufferManager并对其手动调用.Clear() - 这是从最优解远,但这对我来说已经有所帮助了,它不仅能够在一个服务实例中释放前述的64M,而且还能够释放64M + 32M + 16M + 8M + 4M + 2M!因此,只有这样,才能让我的应用持续更长的时间,而不会遇到内存问题(并且不,我们没有内存泄漏问题,除了BufferManager,尽管我们消耗了大量内存并在课程中积累了大量数据很多天,但这不是问题)
@ dlev's编辑:感谢您消除幽默。我很感激。 –
请问每个问题有一个问题。 –
@John Saunders - 并将样板复制到这些单独问题的每一个中?这似乎没有道理。尽管我把问题分开了,但它们都涉及到同样的问题。有人可能会说至少分裂了“一般”和“具体”的问题,但是,即使是一般性问题也涉及到我的具体情况,所以... –