Подтвердить что ты не робот

.NET WCF w3wp утечка памяти и 18k динамических сборок 0 размеров в кучке загрузчика

Наша служба 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.

  • Процесс был сбор мусора, поэтому, по предупреждению, я не должен доверять всем выводам команд! heap.

  • Куча Gc: 1,37 ГБ

  • Размер кэша .NET - 750 МБ,

    Сведения о виртуальной памяти: Виртуальные размещения: 17,45 Гб Загруженные модули: 208.68 Mb Темы: 25 Mb Родные кучи: 3,06 Гб (меня это беспокоит.)

Сверху 3.02 Gb присутствует только на Heap 0x003f0000. У нас хороший объем трафика, так что размер кучи размером 1.3 gb Gc чувствует себя нормально для меня. Также у нас есть машина с 32 gb ram и 64-битным адресным пространством, поэтому допустим размер кэша 750 mb. По размеру "Нативной кучи" я чувствую, что это утечка памяти.

Предупреждение DebugDiag: В файле дампа загружены 18149 динамических сборок.

Ссылки справки:
Утечка .NET Memory: XmlSerializing ваш путь к утечке памяти
Анализ. Мы используем XmlSerialisers, но они кэшированы, поэтому они создаются только один раз.

Утечка .NET Memory: XslCompiledTransform и пропущенные динамические сборки
У нас, похоже, есть такая же проблема, описанная в этом сообщении в блоге. Все эти 18149 динамические сборки имеют 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 ~)

Я предполагаю, что эти просочившиеся динамические сборки занимают 150 Мб пространства. Не уверены, связано ли это 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 Finalizer, это не так, из-за трассировки стека. Он ждет события завершения.

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.Reflection.Emit.InternalAssemblyBuilder, что и утечка динамических сборок.

Я заметил System.Reflection.Emit.DynamicResolver в верхней очереди финализатора и сбросил все из них и сопоставил их с динамическим адресом сборки следующим образом.

Сбрасывается вокруг 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, может кто-нибудь дать мне некоторые подсказки.

  • Как связать выше динамические модули, чтобы найти код ошибки. Я считаю, что это связано с выражением Linq или Lambda.
  • Согласно отчету, размер динамических сборок составляет 150 Мбайт, утечка 3 Гб будет отличаться или динамические модули могут связываться с какой-то собственной памятью.

! heap -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
4b9b3361

Ответ 1

WCF автоматически генерирует классы сериализации в памяти для некоторых протоколов связи, в основном для обмена XML, и, похоже, создает другой класс для каждого возможного изменения структуры сообщений; это легко объясняет количество сборок. Это поведение, по-видимому, является нормальным для XML-протоколов WCF. Если у вас есть контроль над протоколом, переход на не-XML-коммуникационный протокол может решить эту проблему.

Потребление памяти 3 ГБ разумно для этого - динамическая сборка будет существовать как на MSIL (язык ассемблера .NET), так и на родных версиях в памяти. 150MB, вероятно, для версии MSIL, которая была первоначально сгенерирована WCF, и не включает в себя собственный код, который генерируется компилятором .NET JIT, как только версия MSIL "загружается" в качестве модуля и делает вызываемым.

Виртуальное пространство 17.45GB не является реальной памятью, а вместо этого является расстоянием между наименьшими и наивысшими ячейками памяти, где эти модули загружаются; например, если основной модуль загружается со смещением 0, а первая динамическая сборка загружается с 00000000: 0b000000, общая виртуальная память, указанная в списке, составит около 185 МБ, даже если основной модуль занимает всего 2 МБ, а динамическая сборка занимает еще 2 МБ. Это преувеличенный пример, поскольку они обычно упакованы достаточно хорошо, но 1MB типичен между адресами, поэтому 18000 * 1MB = 18000MB, который разделен на 1024, дает ровно 17 ГБ адресного пространства (добавьте еще одну половину GB для остальной части вашей системы и у вас есть полное виртуальное адресное пространство).

Я также видел еще один тип быстрорастущей утечки памяти с WCF: если какая-либо часть сообщения хранится постоянным компонентом приложения, базовый объект XML COM не будет собираться с мусором, что приведет к большое количество утечек памяти..NET XML DOM использует подсистему Windows COM XML, которая является родным кодом и выделяет нативную кучу. Это может объяснить разницу между управляемыми и внутренними кучами памяти. Взгляните на фактическую память в дампе (найдите инструмент, который может отфильтровать печатный текст), и проверьте, сколько из него отформатировано XML.

Я видел, что оба эти утечки происходят, и быстро потребляют всю память на довольно крупномасштабных серверах, поэтому я надеюсь, что мой опыт дает вам ответ или, по крайней мере, дает вам дополнительные подсказки для отслеживания вашей проблемы.

Ответ 2

Чтобы диагностировать, что использует большой объем памяти, вы хотите использовать

!dumpheap -stat.

Это суммирует использование объекта, суммируя количество экземпляров. Одной областью, которая хранит память, является куча больших объектов (что-то более 85 тыс.). Эта область не будет GC'd, пока она не будет абсолютно необходима.

Чтобы посмотреть конкретно на LOH, вы можете использовать:

!dumpheap -stat -min 85000

Для элементов, о которых вы говорите выше, вам нужно узнать, в каком поколении они находятся. Это можно сделать, найдя адрес элемента, а затем просмотрев вывод !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

Если в вашем примере есть Gen 3, вам нужно будет изучить, в какой структуре данных он находится. 85k + непрерывный обычно byte[] или string s.

Ответ 3

Мы видели проблемы с наступлением и НЕ используем потокобезопасные коллекции в коде. Поскольку большинству людей нравится читать данные в коллекциях, это, по-видимому, является "повторной" проблемой с протекцией и т.д.

См. список коллекций потокобезопасных файлов здесь: Тематические сборники

Еще одна вещь, которую нужно добавить. Мы используем профилировщик RedGate/ANTS. Также проверьте подключение mgmt и очистку кода службы WCF.

Ответ 4

Вероятно, это будет какой-то лямбда-запрос, неизвестный во время компиляции.

Возможно, разработчики могут взглянуть на добавление Microsoft Azure Application Insights на соответствующий веб-сайт. Затем вы можете найти, какая страница имеет длинный код на нем, чтобы вы могли сузить их на пораженный код.

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

Ответ 5

Как дикое предположение, я могу сказать, что буферизованные (по умолчанию) службы WCF содержат много дополнительной памяти. Если вы переключились на потоковое вещание, вы можете получить огромную экономию памяти.

См. статью https://msdn.microsoft.com/en-us/library/ms731913(v=vs.110).aspx.

В одном случае моя служба WCF уменьшила использование памяти на 512 миллионов, переключившись на буферизацию.