Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Эта статья относится к: ✔️ пакету SDK для .NET Core 3.1 и более поздних версий
Память может утечь, когда приложение ссылается на объекты, которые больше не требуется выполнять нужную задачу. Ссылка на эти объекты предотвращает восстановление используемой памяти сборщиком мусора. Это может привести к снижению производительности и возникновению OutOfMemoryException исключения.
В этом руководстве показаны средства для анализа утечки памяти в приложении .NET с помощью средств командной строки диагностики .NET. Если вы находитесь в Windows, вы можете использовать средства диагностики памяти Visual Studio для отладки утечки памяти.
В этом руководстве используется пример приложения, которое намеренно допускает утечки памяти как упражнение. Вы также можете анализировать приложения, которые непреднамеренно теряют память.
В этом руководстве описано следующее:
- Проверьте использование управляемой памяти, используя dotnet-counters.
- Создайте файл дампа.
- Анализ использования памяти с помощью файла дампа.
Предпосылки
В руководстве используются следующие элементы:
- Пакет SDK для .NET Core 3.1 или более поздняя версия.
- dotnet-counters для проверки использования управляемой памяти.
- dotnet-dump для сбора и анализа файла дампа (включает расширение отладки SOS).
- Пример целевого приложения отладки для диагностики.
В этом руководстве предполагается, что примеры приложений и инструментов установлены и готовы к использованию.
Если приложение работает с версией .NET более ранней, чем .NET 9, выходной пользовательский интерфейс dotnet-counters будет выглядеть немного иначе; Дополнительные сведения см. в счетчиках dotnet-counters .
Изучение использования управляемой памяти
Прежде чем приступить к сбору диагностических данных, чтобы помочь в первопричине этого сценария, убедитесь, что вы на самом деле видите утечку памяти (рост использования памяти). Для подтверждения этого можно использовать средство dotnet-counters .
Откройте окно консоли и перейдите в каталог, в котором вы скачали и распаковали пример отладочной цели. Запустите целевой объект:
dotnet run
В отдельной консоли найдите идентификатор процесса:
dotnet-counters ps
Выходные данные должны выглядеть следующим образом:
4807 DiagnosticScena /home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios
Замечание
Если предыдущая команда не работает или не найдена, скорее всего, необходимо сначала установить dotnet-counters средство. Используйте следующую команду:
dotnet tool install --global dotnet-counters
Теперь проверьте использование управляемой памяти с помощью средства dotnet-counters . Указывает --refresh-interval количество секунд между обновлениями:
dotnet-counters monitor --refresh-interval 1 -p 4807
Выходные данные в реальном времени должны быть похожи на:
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[System.Runtime]
dotnet.assembly.count ({assembly}) 111
dotnet.gc.collections ({collection})
gc.heap.generation
------------------
gen0 1
gen1 0
gen2 0
dotnet.gc.heap.total_allocated (By) 4,431,712
dotnet.gc.last_collection.heap.fragmentation.size (By)
gc.heap.generation
------------------
gen0 803,576
gen1 15,456
gen2 0
loh 0
poh 0
dotnet.gc.last_collection.heap.size (By)
gc.heap.generation
------------------
gen0 811,960
gen1 1,214,720
gen2 0
loh 0
poh 24,528
dotnet.gc.last_collection.memory.committed_size (By) 4,296,704
dotnet.gc.pause.time (s) 0.003
dotnet.jit.compilation.time (s) 0.329
dotnet.jit.compiled_il.size (By) 120,212
dotnet.jit.compiled_methods ({method}) 1,202
dotnet.monitor.lock_contentions ({contention}) 2
dotnet.process.cpu.count ({cpu}) 22
dotnet.process.cpu.time (s)
cpu.mode
--------
system 0.344
user 0.344
dotnet.process.memory.working_set (By) 64,331,776
dotnet.thread_pool.queue.length ({work_item}) 0
dotnet.thread_pool.thread.count ({thread}) 0
dotnet.thread_pool.work_item.count ({work_item}) 7
dotnet.timer.count ({timer}) 0
Сосредоточьте внимание на этой строке:
dotnet.gc.last_collection.memory.committed_size (By) 4,296,704
Вы увидите, что память управляемой кучи составляет 4 МБ сразу после запуска.
Теперь перейдите по URL-адресу https://localhost:5001/api/diagscenario/memleak/20000.
Обратите внимание, что использование памяти выросло до 20 МБ.
dotnet.gc.last_collection.memory.committed_size (By) 21,020,672
Наблюдая за использованием памяти, вы с уверенностью можете утверждать, что память увеличивается или протекает. Следующим шагом является сбор правильных данных для анализа памяти.
Создание дампа памяти
При анализе возможных утечек памяти необходимо получить доступ к куче памяти приложения для анализа содержимого памяти. Глядя на связи между объектами, вы создаете теории о том, почему память не освобождается. Распространенный источник диагностических данных — это дамп памяти в Windows или эквивалентный основной дамп в Linux. Для создания дампа приложения .NET можно использовать средство dotnet-dump .
Используя пример целевого объекта отладки , запущенного ранее, выполните следующую команду, чтобы создать дамп ядра Linux:
dotnet-dump collect -p 4807
Результатом является основной дамп, расположенный в той же папке.
Writing minidump with heap to ./core_20190430_185145
Complete
Замечание
Для сравнения с течением времени позвольте исходному процессу продолжить работу после сбора первого дампа и сбора второго дампа таким же образом. Затем у вас будет два дампа за определенный промежуток времени, которые вы можете сравнить, чтобы увидеть, где увеличивается использование памяти.
Перезапустите неудачный процесс
После сбора дампа у вас будет достаточно информации для диагностики процесса, завершившегося сбоем. Если неработающий процесс запущен на производственном сервере, сейчас идеальное время для его краткосрочного исправления путем перезапуска процесса.
В этом руководстве вы уже выполнили работу с целевым объектом отладки sample , и его можно закрыть. Перейдите к терминалу, на который запущен сервер, и нажмите клавиши CTRL+C.
Анализ дампа памяти
Теперь, когда вы создали основной дамп, используйте средство dotnet-dump для анализа дампа:
dotnet-dump analyze core_20190430_185145
Где core_20190430_185145 находится имя основного дампа, который требуется проанализировать.
Замечание
Если вы видите ошибку, жалующуюся на то, что libdl.so не удается найти, может потребоваться установить пакет libc6-dev . Дополнительные сведения см. в разделе "Предварительные требования для .NET" в Linux.
Появится запрос, в котором можно ввести команды SOS . Как правило, первое, что вы хотите посмотреть, — это общее состояние управляемой кучы:
> dumpheap -stat
Statistics:
MT Count TotalSize Class Name
...
00007f6c1eeefba8 576 59904 System.Reflection.RuntimeMethodInfo
00007f6c1dc021c8 1749 95696 System.SByte[]
00000000008c9db0 3847 116080 Free
00007f6c1e784a18 175 128640 System.Char[]
00007f6c1dbf5510 217 133504 System.Object[]
00007f6c1dc014c0 467 416464 System.Byte[]
00007f6c21625038 6 4063376 testwebapi.Controllers.Customer[]
00007f6c20a67498 200000 4800000 testwebapi.Controllers.Customer
00007f6c1dc00f90 206770 19494060 System.String
Total 428516 objects
Здесь можно увидеть, что большинство объектов — это String или Customer объекты.
Чтобы получить список всех dumpheap экземпляров, можно снова использовать String команду с таблицей методов (MT):
> dumpheap -mt 00007f6c1dc00f90
Address MT Size
...
00007f6ad09421f8 00007faddaa50f90 94
...
00007f6ad0965b20 00007f6c1dc00f90 80
00007f6ad0965c10 00007f6c1dc00f90 80
00007f6ad0965d00 00007f6c1dc00f90 80
00007f6ad0965df0 00007f6c1dc00f90 80
00007f6ad0965ee0 00007f6c1dc00f90 80
Statistics:
MT Count TotalSize Class Name
00007f6c1dc00f90 206770 19494060 System.String
Total 206770 objects
Теперь можно использовать команду gcroot на экземпляре System.String, чтобы понять, как и почему объект удерживается в памяти:
> gcroot 00007f6ad09421f8
Thread 3f68:
00007F6795BB58A0 00007F6C1D7D0745 System.Diagnostics.Tracing.CounterGroup.PollForValues() [/_/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs @ 260]
rbx: (interior)
-> 00007F6BDFFFF038 System.Object[]
-> 00007F69D0033570 testwebapi.Controllers.Processor
-> 00007F69D0033588 testwebapi.Controllers.CustomerCache
-> 00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
-> 00007F6C000148A0 testwebapi.Controllers.Customer[]
-> 00007F6AD0942258 testwebapi.Controllers.Customer
-> 00007F6AD09421F8 System.String
HandleTable:
00007F6C98BB15F8 (pinned handle)
-> 00007F6BDFFFF038 System.Object[]
-> 00007F69D0033570 testwebapi.Controllers.Processor
-> 00007F69D0033588 testwebapi.Controllers.CustomerCache
-> 00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
-> 00007F6C000148A0 testwebapi.Controllers.Customer[]
-> 00007F6AD0942258 testwebapi.Controllers.Customer
-> 00007F6AD09421F8 System.String
Found 2 roots.
Видно, что объект String находится непосредственно в объекте Customer и косвенно удерживается CustomerCache объектом.
Вы можете продолжать выгружать объекты, чтобы увидеть, что большинство String объектов соответствуют аналогичному шаблону. На этом этапе исследование предоставило достаточную информацию, чтобы определить первопричину в коде.
Эта общая процедура позволяет определить источник утечки памяти.
Очистите ресурсы
В этом руководстве вы запустили пример веб-сервера. Этот сервер должен был быть выключен, как описано в разделе Перезапуска неудачного процесса.
Вы также можете удалить созданный файл дампа.
См. также
- dotnet-trace для вывода списка процессов
- dotnet-counters для проверки использования управляемой памяти
- dotnet-dump для сбора и анализа файла дампа
- dotnet/diagnostics
- Отладка утечек памяти с помощью Visual Studio