.NET WCF w3wp本地内存泄漏和加载程序堆中的大小为0的18kdynamic程序集

我们的WCF服务显示了大量内存使用的实例,所以我们花了一个完整的内存转储来确定问题。

Operating System Windows Server 2008 R2Service Pack 1 Number Of Processors 4 Process Image c:\Windows\System32\inetsrv\w3wp.exe System Up-Time 40 day(s) 09:23:09 Process Up-Time 14 day(s) 11:49:01 .NET 4.0 Processor Type X64 Process Bitness 64-Bit 

从DebugDiag报告的问题的直升机视图。

  1. 进程是垃圾收集,所以根据警告我不应该信任!heap命令的所有输出。

  2. Gc堆:1.37 GBytes

  3. .NETcaching大小为750Mb,

    虚拟内存详细信息:虚拟分配:17.45 Gb加载模块:208.68 Mb线程数:25 Mb本地堆积:3.06 Gb(我很关心这个)

从上面的3.02 Gb出现在Heap 0x003f0000 。 我们有很好的stream量,这样1.3 gb Gb的堆大小感觉对我来说是正常的。 此外,我们有32 gb RAM和64位地址空间的机器,所以caching大小750 mb是可以接受的。 根据本地堆的大小,我觉得这是本机内存泄漏。

DebugDiag警告: 在转储文件中加载了18149个dynamic程序集。

帮助链接:
.NET内存泄漏:XmlSerializing您的方式到内存泄漏
分析 – 我们使用XmlSerialisers,但它们被caching,这样它们只被创build一次。

.NET内存泄漏:XslCompiledTransform和泄漏的dynamic程序集
我们似乎有这种博客文章中描述的相同types的问题。 所有这些18149dynamic组件都是0大小的。 所以我不能转储他们得到的细节。 此外,我们不使用Xsl转换任何地方在这个应用程序。 所以这些程序集不是由于Xsl转换。

更多的数据:
相关对象数量:

 System.Reflection.Emit.InternalModuleBuilder ----- 1.11 MBytes (18149 objects ) System.Reflection.Emit.InternalAssemblyBuilder ----- 992.52 KBytes (18149 objects ) System.Reflection.Emit.__FixupData[] ---------- 595.41 KBytes (752 objects ) System.Reflection.Emit.GenericFieldInfo ---------- 580.03 KBytes (18561 objects ) System.Reflection.RuntimeMethodInfo ---------- 1.2 MBytes (11276 objects ) System.RuntimeType -------------------- 1.13 MBytes (21228 objects ) 

Finalizer队列中的顶层对象

System.Reflection.Emit.DynamicResolver - 379 System.Reflection.Emit.DynamicResolver+DestroyScout - 271

应用程序域统计
Domain - Default - 13 assemblies - size : 89,677,824 (90 Mb ~) Domain - ROOT/tv/Engine1 - 18236 Assemblies- Size :152,834,048 (150 Mb ~)

我猜这些泄露的dynamic程序集占了150 Mb的空间。 不确定3GB的本机内存是否是由于这些程序集?

用这个程序集来挖掘更多:

!dumpdomain给我很大的未知dynamic程序集如下:

 Assembly: 000000000fa9d0d0 (Dynamic) [] ClassLoader: 0000000002be1d40 SecurityDescriptor: 000000000fc08a00 Module Name 000007fe96d38e68 Dynamic Module and !EEHeap -loader gives same number of 0 sized modules : Module 000007fea0b7b758: Size: 0x0 (0) bytes. Module 000007fea0b7c1e8: Size: 0x0 (0) bytes. Module 000007fea0b7cc78: Size: 0x0 (0) bytes. 

从下面的堆栈跟踪检查阻塞的GC终结器线程是不是这种情况。 正在等待Finalization事件发生。

 0:000> ~20 k Child-SP RetAddr Call Site 00000000`0eedf3b8 000007fe`fd6f1430 ntdll!ZwWaitForMultipleObjects+0xa 00000000`0eedf3c0 00000000`77501723 KERNELBASE!WaitForMultipleObjectsEx+0xe8 00000000`0eedf4c0 000007fe`f60939d4 kernel32!WaitForMultipleObjectsExImplementation+0xb3 00000000`0eedf550 000007fe`f6094799 clr!SVR::WaitForFinalizerEvent+0xcc 00000000`0eedf590 000007fe`f5f0458c clr!SVR::GCHeap::FinalizerThreadWorker+0x4a 00000000`0eedf5d0 000007fe`f5f0451a clr!Frame::Pop+0x50 

转储具有与泄漏的dynamic程序集相同数量的System.Reflection.Emit.InternalModuleBuilderSystem.Reflection.Emit.InternalAssemblyBuilder对象。

我注意到在顶部终结器队列中的System.Reflection.Emit.DynamicResolver并将其全部转存,并将它们与dynamic程序集地址关联如下。

DynamicResolver -> m_method -> m_module (00000001801728a0) 5个DynamicResolver对象并跟踪DynamicResolver -> m_method -> m_module (00000001801728a0)

00000001801728a0这是来自InternalModuleBuilder列表的一个模块的地址。 他们中的大多数都指向相同的模块。

 0:000> !dumpheap -type System.Reflection.Emit.DynamicResolver Address MT Size 000000018017d5a8 000007fef4c7c8b0 72 000000018018d5b0 000007fef4c7c8b0 72 00000001801931b0 000007fef4c7c8b0 72 ------- and on 0:000> !do 000000018017d5a8 Name: System.Reflection.Emit.DynamicResolver MethodTable: 000007fef4c7c8b0 EEClass: 000007fef4754300 Size: 72(0x48) bytes File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 000007fef4c44458 4002aaa 8 System.Object[] 0 instance 0000000000000000 m_exceptions 000007fef4c9a690 4002aab 10 System.Byte[] 0 instance 0000000000000000 m_exceptionHeader 000007fef4ca20c0 4002aac 18 ...mit.DynamicMethod 0 instance 0000000180172690 m_method 000007fef4c9a690 4002aad 20 System.Byte[] 0 instance 000000018017d5f0 m_code 000007fef4c9a690 4002aae 28 System.Byte[] 0 instance 000000018017d650 m_localSignature 000007fef4c992b8 4002aaf 38 System.Int32 1 instance 3 m_stackSize 000007fef4c7c788 4002ab0 30 ...Emit.DynamicScope 0 instance 0000000180172b80 m_scope 0:000> !do 0000000180172690 Name: System.Reflection.Emit.DynamicMethod MethodTable: 000007fef4ca20c0 EEClass: 000007fef475e298 Size: 112(0x70) bytes File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 000007fef4c44458 4002ac6 8 System.Object[] 0 instance 0000000180172700 m_parameterTypes 000007fef4cafa88 4002ac7 10 ...RuntimeMethodInfo 0 instance 000000018017d678 m_methodHandle 000007fef4c987f8 4002ac8 18 System.RuntimeType 0 instance 00000004800e7900 m_returnType 000007fef4c7c578 4002ac9 20 ...ynamicILGenerator 0 instance 0000000180172a30 m_ilGenerator 000007fef4c4eb18 4002aca 28 ...mit.DynamicILInfo 0 instance 0000000000000000 m_DynamicILInfo 000007fef4c97de0 4002acb 60 System.Boolean 1 instance 1 m_fInitLocals 000007fef4c9f1d8 4002acc 30 ...ion.RuntimeModule 0 instance 00000001801728a0 m_module 000007fef4c97de0 4002acd 61 System.Boolean 1 instance 0 m_skipVisibility 000007fef4c987f8 4002ace 38 System.RuntimeType 0 instance 0000000000000000 m_typeOwner 000007fef4c7c330 4002acf 40 ...d+RTDynamicMethod 0 instance 00000001801729d8 m_dynMethod 000007fef4c7c8b0 4002ad0 48 ...t.DynamicResolver 0 instance 000000018017d5a8 m_resolver 000007fef4c97de0 4002ad1 62 System.Boolean 1 instance 0 m_profileAPICheck 000007fef4c99d18 4002ad2 50 ...n.RuntimeAssembly 0 instance 0000000000000000 m_creatorAssembly 000007fef4c97de0 4002ad3 63 System.Boolean 1 instance 1 m_restrictedSkipVisibility 000007fef4c88d70 4002ad4 58 ...g.CompressedStack 0 instance 00000001801729b0 m_creationContext 000007fef4c88020 4002ad5 16b8 ...rnalModuleBuilder 0 shared static s_anonymouslyHostedDynamicMethodsModule >> Domain:Value 0000000002b66ba0:NotInit 0000000002c24a90:00000001801728a0 << 000007fef4c96ae8 4002ad6 16c0 System.Object 0 shared static s_anonymouslyHostedDynamicMethodsModuleLock >> Domain:Value 0000000002b66ba0:NotInit 0000000002c24a90:0000000180172798 << Opened log file 'C:\debug\new_dynamic_asm.log' 0:000> !dumpheap -type System.Reflection.Emit.InternalModuleBuilder Address MT Size 00000001800fe918 000007fef4c88020 64 00000001801728a0 000007fef4c88020 64 000000018017fa88 000007fef4c88020 64 00000001801bee20 000007fef4c88020 64 ------- and on 

我用WinDbg不太方便,有人可以给我一些线索。

  1. 如何把上面的dynamic模块联系起来以得到bug代码。 我相信这是由于Linq或Lambda的expression。
  2. 根据报告,dynamic程序集的大小是150 Mb,3 Gb泄漏将是任何不同的或dynamic模块可能链接到一些本地内存。

!堆 – 我给了我188722潜在的不可达的块被发现。

使用WinDbg PyKd插件的本地堆统计数据给了我下面本机堆的统计信息。

请注意,这些值大概在18000左右

 Statistics: Type name Count Size clr!RecordPool 817335 Unknown clr!RegMeta 272445 Unknown clr!CBlobPoolHash 36326 Unknown clr!MDInternalRW 36326 Unknown clr!StgBlobPool 36326 Unknown clr!CCeeGen 36298 Unknown clr!PEAssembly 18267 Unknown clr!AssemblySecurityDescriptor 18249 Unknown clr!DomainAssembly 18249 Unknown clr!SharedSecurityDescriptor 18236 Unknown clr!CStringPoolHash 18163 Unknown clr!CMiniMdRW 18163 Unknown clr!StgGuidPool 18163 Unknown clr!StgStringPool 18163 Unknown clr!CCustAttrHash 18163 Unknown clr!CGuidPoolHash 18163 Unknown clr!PESectionMan 18149 Unknown clr!CeeSectionString 18149 Unknown clr!PESection 18149 Unknown nativerd!CONFIG_ELEMENT 4932 Unknown nativerd!ATTRIBUTE_VALUE 3912 Unknown nativerd!SCHEMA_ATTRIBUTE 1473 Unknown clr!CAssemblyName 1116 Unknown nativerd!COLLECTION_KEY_ENTRY 919 Unknown nativerd!SCHEMA_ELEMENT 766 Unknown clr!AssemblyMDInternalImport 720 Unknown nativerd!CONFIG_SECTION 652 Unknown nativerd!CONFIG_COLLECTION 570 Unknown clr!ListNode<CHashNode * __ptr64> 444 Unknown 

WCF自动为内存中的某些通信协议生成序列化类,主要用于XML通信,并且似乎为消息结构中的每个可能的变体创build了不同的类; 这很容易解释组件的数量。 对于基于XML的WCF协议,这种行为显然是正常的。 如果您控制了协议,则切换到非XML通信协议可能会解决此问题。

3GB的内存消耗是合理的 – dynamic程序集将存在MSIL(.NET汇编语言)和内存中的本地版本。 150MB可能是最初由WCF生成的MSIL版本,只要MSIL版本作为模块“加载”并且可调用,就不包括由.NET JIT编译器生成的本机代码。

17.45GB的虚拟空间不是真实的内存,而是这些模块加载的最低和最高内存位置之间的距离; 例如,如果主模块在偏移量0加载,并且第一个dynamic程序集在00000000:0b000000加载,则总共列出的虚拟内存将大约为185MB,即使主模块只需要2MB,dynamic程序集需要另外2MB。 这是一个夸张的例子,因为它们通常打包得很好,但是在地址之间通常是1MB,所以18000 * 1MB = 18000MB,除以1024就得到了17GB的地址空间(为系统的其余部分增加了另外的半个GB)而且你有完整的虚拟地址空间)。

我还看到了另外一种types的快速增长的WCF内存泄漏:如果消息的任何部分由应用程序的持久组件持有,底层的XML COM对象将不会被垃圾收集,从而导致相当大量的内存泄漏。 .NET XML DOM使用Windows COM XML子系统,这是本地代码,并分配在本机堆上。 这可以解释托pipe和本地内存堆之间的差异。 查看转储中的实际内存(查找可以过滤掉可打印文本的工具),然后检查它的格式化XML是多less。

我已经看到这两个漏洞都会发生,并且会迅速占用相当大规模的服务器上的所有内存,所以我希望我的经验为您提供一个答案,或者至less为您提供一些追踪问题的额外提示。

要诊断什么是使用大量的内存,你想使用

!dumpheap -stat

这将通过总结实例的数量来总结对象的使用情况。 囤积记忆的一个领域是大对象堆(超过85k)。 直到绝对必要之前,这个地区不会被GC'd。

要专门查看LOH,您可以使用:

!dumpheap -stat -min 85000

对于你所关心的项目,你需要找出他们在哪一代。这可以通过查找项目的地址,然后像这样查看!DumpObject的输出来!DumpObject

 > 0:000> !do 0x0000000011b47450 Name: System.Byte[] MethodTable: 000007fef7d2e798 EEClass: 000007fef7932670 Size: 131096(0x20018) bytes GC Generation: 3 <--- ****** Gen 3 == LOH Array: Rank 1, Number of elements 131072, Type Byte Element Type: System.Byte Fields: None 

如果在你的例子中是第3代,你将需要看看它是什么数据结构。 连续的85k +通常是byte[]string s。

我们已经看到了在代码中使用线程安全集合的问题。 由于大多数人喜欢将数据读入集合,所以这似乎是踩踏等“重复”的问题。

在这里查看线程安全集合列表: 线程安全集合

还有一件事要补充。 我们使用RedGate / ANTS分析器。 同时检查您的连接pipe理和清理WCF服务代码。

这可能是某种在编译时不知道的lambda查询。

您可能会让开发人员考虑将Microsoft Azure的Application Insights添加到相关网站。 然后你可以find哪个页面上有很长的运行代码,这样你可以缩小受影响的代码。

https://azure.microsoft.com/en-us/documentation/articles/app-insights-start-monitoring-app-health-usage/

正如一个疯狂的build议,我可以告诉你缓冲(默认)WCF服务吃了很多额外的内存。 如果您切换到stream式,您可以节省大量的内存。

看到这篇文章https://msdn.microsoft.com/en-us/library/ms731913(v=vs.110).aspx 。

在一种情况下,我的WCF服务通过切换到缓冲减less了512meg的内存使用量。