Eug*_*sky 13 .net c# wcf buffer-manager
分析一个WCF客户端应用程序(我没有写,还是不知道太多),其通过SOAP和运行一两天就会抛出一个OutOfMemoryException后谈了一堆的服务,我发现.NET的PooledBufferManager会永远不会释放未使用的缓冲区,即使应用程序内存不足,也会导致OOME.
这当然符合规范:http://msdn.microsoft.com/en-us/library/ms405814.aspx
当垃圾收集回收缓冲池时,池及其缓冲区被销毁.
请随意回答下面的一个问题,因为我有一堆问题,一些更一般的问题,以及一些特定于我们的应用程序使用BufferManager.
首先是关于(默认的Pooled)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,通常多秒甚至几分钟.
现在有一些更具体的问题与我们的应用程序使用BufferManagers有关.该应用程序连接到几个不同的WCF服务.对于它们中的每一个,我们为http连接维护一个连接池,因为连接可能同时发生.
检查一个堆转储中的单个最大对象,一个64M字节数组,在初始化时只在我们的应用程序中使用过一次,之后不再需要,因为来自服务的响应仅在初始化时很大,即btw.对于我使用过的许多应用程序来说都是典型的,即使这可能会受到优化(缓存到磁盘等).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[])
Run Code Online (Sandbox Code Playgroud)
查看由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)或者甚至更好,我们是否可以告诉所有HttpsChannelFactory实例根本不使用BufferManagers并要求GC完成它的神秘工作,即"管理内存"?
There is a MaxBufferPoolSize binding parameter, which controls max size of buffers in BufferManager. Setting it to 0 will disable buffering, and GCBufferManager will be created instead of pooled one - and it will GC allocated buffers as soon as message is processed, as in your question.
This article discusses WCF memory buffer management in greater detail.
4)我们在这里做错了吗?我不是任何WCF专家,所以我们可以使各种HttpsChannelFactory实例都使用相同的BufferManager吗?
5)或者甚至更好,我们是否可以告诉所有HttpsChannelFactory实例根本不使用BufferManagers并要求GC完成它的神秘工作,即"管理内存"?
我想解决这两个问题的一种方法是将TransferMode从'buffered'改为'stream'.将不得不调查,因为'流式'模式有一些限制,我可能无法使用它.
更新:它确实很棒!在应用程序启动期间,我在缓冲模式下的内存消耗在高峰时间为630M,在满载时减少到470M.切换到流模式后,内存消耗不会显示临时峰值,完全加载后,功耗仅为270M!
顺便说一句,这对我来说是客户端应用程序代码的单行更改.我只需要添加这一行:
httpsTransportBindingElement.TransferMode = TransferMode.StreamedResponse;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
8615 次 |
最近记录: |