создать файл дампа из моего процесса приложения из диспетчера задач при сбое приложения
Это не хороший выбор, потому что
- у тебя мало времени для этого. Это можно сделать только до тех пор, пока отображается диалоговое окно сбоя. Если вы опоздали, приложение пропало.
- в этом состоянии у вас будут трудности с его отладкой. Вместо исходного исключения будет отображаться точка останова, которая используется ОС для отображения диалогового окна и сбора диагностических данных
Используйте локальные дампы WER, чтобы автоматически создавать аварийный дамп при сбое, вместо того, чтобы делать это вручную. Это гораздо надежнее и дает вам оригинальное исключение. См. Как получить хороший аварийный дамп для .NET
Я нахожусь в правильном направлении в поиске проблемы утечки памяти?
Извините, вы уже не на том пути.
Начиная с !dumpheap -stat
не очень хорошая идея. Обычно можно начать с самого низкого уровня, который составляет !address -summary
. Он покажет вам, является ли это утечкой управляемой памяти или утечкой собственной памяти. Если это управляемая утечка, вы можете продолжить с !dumpheap -stat
если мой путь правильный, где мой следующий шаг?
Даже если это не правильный путь, неплохо бы узнать, как понять, что вы на неправильном пути. Итак, откуда я знаю?
Глядя на ваш вывод !dumpheap -stat
, вы можете увидеть
[...]
111716 12391360 System.String.
Это говорит о том, что есть 110 000 различных строк, использующих 12 МБ памяти. Это также говорит вам, что все остальное занимает менее 12 МБ. Посмотрите на другие размеры, и вы обнаружите, что .NET не является причиной вашего исключения OutOfMemoryException. Они используют менее 50 МБ.
Если бы произошла управляемая утечка, вы бы искали пути, к которым подключены объекты, чтобы сборщик мусора думал, что он не может быть освобожден. Команда !gcroot
.
Средство Windbg подходит для поиска подобных проблем?
Возможно, но WinDbg - не лучший инструмент. Вместо этого используйте профилировщик памяти. Это специальный инструмент для утечки памяти. Как правило, он намного удобнее в использовании. К сожалению, вам нужно решить, нужен ли вам профилировщик управляемой памяти, профилировщик встроенной памяти или оба.
Однажды я написал , как использовать WinDbg для отслеживания .NET OutOfMemoryException . Там вы найдете таблицу, которая дает вам идеи о том, как действовать в различных ситуациях.
В вашем дампе я вижу 2 ТБ <unknown>
памяти, которая может быть .NET, но не обязательно должна быть . Тем не менее, эти 2 ТБ, вероятно, являются причиной ООМ, поскольку размер остальных составляет менее 350 МБ.
Поскольку clr
находится в списке загруженных модулей, мы можем проверить !dumpheap -stat
, как вы это сделали. Но не так много объектов, использующих память.
!eeheap -gc
показывает, что есть 8 куч, соответствующих 8 процессорам вашей машины, для параллельной сборки мусора. Самая большая отдельная куча составляет 45 МБ, всего 249 МБ. Это примерно соответствует сумме !dumpheap
. вывод: .NET не виновник.
Давайте проверим особые случаи:
- Присутствие MSXML
- Bitmaps
- Звонки на
HeapAlloc()
, которые настолько велики, что они напрямую переадресовываются на VirtualAlloc()
.
- Прямые звонки на
VirtualAlloc()
MSXML отсутствует: lm m msxml*
не производит вывод.
Нет растровых изображений: !dumpheap -stat -type Bitmap
Распределение кучи больше 512 кБ: !heap -stat
. Вот усеченная часть вывода:
0:000> !heap -stat
_HEAP 0000018720bd0000
Segments 00000006
Reserved bytes 0000000001fca000
Committed bytes 0000000001bb3000
VirtAllocBlocks 00000002
VirtAlloc bytes 00000312cdc4b110
_HEAP 0000018bb0fe0000
Segments 00000005
Reserved bytes 0000000000f0b000
Committed bytes 0000000000999000
VirtAllocBlocks 00000001
VirtAlloc bytes 0000018bb0fe0110
Как видите, в VirtualAlloc есть 3 блока. Размер несколько нереальный:
0:000> ? 00000312cdc4b110
Evaluate expression: 3379296514320 = 00000312`cdc4b110
0:000> ? 0000018bb0fe0110
Evaluate expression: 1699481518352 = 0000018b`b0fe0110
Это будет в общей сложности 3,3 ТБ + 1,7 ТБ = 6 ТБ, а не 2 ТБ. Теперь может случиться так, что это ошибка !address
, но 4 ТБ не является общей точкой переполнения.
С помощью !heap -a 0000018720bd0000
вы можете увидеть 2 виртуальных выделения:
Virtual Alloc List: 18720bd0110
0000018bac70c000: 00960000 [commited 961000, unused 1000] - busy (b), tail fill
0000018bad07b000: 00960000 [commited 961000, unused 1000] - busy (b), tail fill
А с !heap -a 0000018bb0fe0000
вы можете увидеть третий:
Virtual Alloc List: 18bb0fe0110
0000018bb1043000: 00400000 [commited 401000, unused 1000] - busy (b), tail fill
Это все относительно небольшие блоки по 4,1 МБ и 9,8 МБ.
Для последней части прямых звонков на VirtualAlloc()
вам необходимо вернуться на уровень !address
. С !address -f:VAR -c:".echo %1 %3"
вы можете увидеть адрес и размер всех <unknown>
регионов. Там вы найдете много записей, множество небольших размеров, некоторые из которых могут быть кучами .NET, несколько 2 ГБ и одно действительно большое выделение
2ГБ:
0x18722070000 0x2d11000
0x18724d81000 0x7d2ef000
0x187a2070000 0x2ff4000
0x187a5064000 0x7d00c000
0x18822070000 0x2dfe000
0x18824e6e000 0x7d202000
0x188a2070000 0x2c81000
0x188a4cf1000 0x7d37f000
0x18922070000 0x2d13000
0x18924d83000 0x7d2ed000
0x189a2070000 0x2f5a000
0x189a4fca000 0x7d0a6000
0x18a22070000 0x2c97000
0x18a24d07000 0x7d369000
0x18aa2070000 0x2d0c000
0x18aa4d7c000 0x7d2f4000
Вполне вероятно, что это кучи .NET (зафиксированная часть + зарезервированная часть).
Большой:
0x7df600f57000 0x1ffec56a000
Информация о нем:
0:000> !address 0x7df600f57000
Usage: <unknown>
Base Address: 00007df6`00f57000
End Address: 00007ff5`ed4c1000
Region Size: 000001ff`ec56a000 ( 2.000 TB)
State: 00002000 MEM_RESERVE
Protect: <info not present at the target>
Type: 00040000 MEM_MAPPED
Allocation Base: 00007df5`ff340000
Allocation Protect: 00000001 PAGE_NOACCESS
Он выглядит как файл отображения памяти объемом 2 ТБ, который не используется (и, следовательно, зарезервирован).
Я не знаю, что делает ваше приложение. Это действительно то, где мне нужно остановить анализ. Я надеюсь, что это было полезно, и вы можете сделать свои выводы и решить проблему.