我们的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报告中直升飞机查看问题。
> < li>
进程是垃圾收集,因此根据警告,我不应该相信来自!堆命令。
垃圾收集堆:1.37
. NET缓存大小为750Mb,
虚拟内存详细信息:虚拟分配:17.45 Gb加载模块:208.68 Mb线程:25 Mb本机堆:3.06 Gb(我很担心这一点。)
从上面开始,3.02 Gb
仅存在于堆0x003f0000
上。我们有很好的流量,这样 1.3 GB
Gc 堆大小对我来说很正常。此外,我们有具有32 gb
RAM和64位地址空间的机器,因此750 mb
的缓存大小是可以接受的。根据本机堆的大小,我觉得这是本机内存泄漏。
DebugDiag 警告:转储文件中加载了 18149 个动态程序集。
帮助链接:
.NET内存泄漏:XmlSerializing您的内存泄漏方法
分析-我们确实使用XmlSerializer,但它们是缓存的,这样它们只创建一次。
. NET内存泄漏:XslCompiledTransform和泄漏的动态程序集
我们似乎遇到了与此博客文章中描述的相同类型的问题。所有这些18149动态程序集的大小为0。所以我无法转储它们以获取详细信息。此外,我们在此应用程序中不使用Xsl转换。所以这些程序集不是由于Xsl转换。
更多统计:< br >相关对象计数:< br >
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 )
终结器队列中的顶级对象< br >
System.Reflection.Emit.DynamicResolver-379
System Reflection.Emit.DynamicSolver DestroyScout-271
应用程序域统计域-默认-13个程序集-大小:89,677,824(90 Mb~)
域-ROOT/tv/Engine 1-18236程序集-大小:152,834,048(150 Mb~)
我猜这些泄漏的动态组件占了150 Mb的空间。不确定这些程序集是否导致了3 Gb的本机内存?
使用此程序集进行更多挖掘:
!dumpdomain
给我大的未知动态程序集,如下所示:
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终结器线程。从下面的堆栈跟踪来看,情况并非如此。它正在等待终结事件发生。< br >
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
转储具有与泄漏的动态程序集相同数量的System.Reflection.Emit.InternalModuleBuilder
和System.Reflecton.Emit.InternalAssemblyBuilder
对象。
我注意到Top终结器队列中的System. Reflection.Emit.DynamicResolver
并转储所有这些并将它们与动态程序集地址相关联,如下所示。
转储大约5个DynamicResolver对象并跟踪DynamicResolver-
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不太在行,有人能给我一些线索吗?
!堆-l给了我188722个潜在的不可访问块被检测到。
使用WinDbg PyKd插件的本地堆统计给了我下面的本地堆统计。
请注意,值在18,000左右滚动
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通信,并且似乎为消息结构中的每个可能变化创建不同的类;这很容易解释程序集的数量。对于基于XML的WCF协议,这种行为显然是正常的。如果您可以控制协议,切换到非XML通信协议可能会解决这个问题。
为此,3GB的内存消耗是合理的——动态程序集将存在于两个MSIL(。NET汇编语言)和内存中的本机版本。150MB可能是最初由WCF生成的MSIL版本,不包括由。一旦MSIL版本作为一个模块被“加载”并成为可调用的,就可以使用. NET JIT编译器。
17.45GB的虚拟空间不是真正的内存,而是这些模块被加载的最低和最高内存位置之间的距离;例如,如果主模块在偏移量0处加载,而第一个动态程序集在00000000:0b000000处加载,则列出的虚拟内存总量将约为185MB,即使主模块仅占用2MB,而动态程序集占用另外2MB。这是一个夸张的例子,因为它们通常打包得很好,但地址之间通常是1MB,因此18000*1MB=18000MB,除以1024正好得到17GB的地址空间(为系统的其余部分再增加一半GB,您就拥有了完整的虚拟地址空间)。
我还看到了另一种使用WCF快速增长的内存泄漏类型:如果消息的任何部分由应用程序的持久组件保存,则底层XML COM对象将不会被垃圾收集,从而导致相当大的内存泄漏。NET XML DOM使用Windows COM XML子系统,这是本机代码,并在本机堆上分配。这可以解释托管和本机内存堆之间的区别。查看转储中的实际内存(寻找可以过滤掉可打印文本的工具),并检查其中有多少是格式化的XML。
我已经看到这两种泄漏都会发生,并且会迅速消耗相当大的服务器上的所有内存,所以我希望我的经验能够为您提供答案,或者至少为您提供一些跟踪问题的额外提示。
要诊断使用大量内存的原因,您需要使用
!dumpheap -stat
.
这将通过对实例数量求和来总结对象使用情况。存储内存的一个区域是大型对象堆(任何超过85k的区域)。除非绝对必要,否则不会对该区域进行GC。
要具体查看LOH,您可以使用:
! duphap-stat-min 85000
对于您上面关注的项目,您需要找出它们属于哪一代。这可以通过查找项目的地址,然后查看 !像这样转储对象
:
> 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
如果在您的示例中是Gen 3,您需要了解它是什么数据结构。85k连续通常是byte[]
或string
s。
我们已经看到了涉足和不在代码中使用线程安全集合的问题。由于大多数人喜欢将数据读入集合,这似乎是涉足等的“重复”问题。
在这里查看线程安全集合列表:线程安全集合
还有一件事要补充。我们使用RedGate/ANTS分析器。还要检查连接管理和WCF服务代码的清理。