Есть ли .Net Memory Profiler, который будет отслеживать все распределения в большой кучей объектов?

Большинство профайлов памяти .NET, которые я пробовал, позволяют делать снимки памяти.

Тем не менее, я пытаюсь диагностировать проблему, в которой я получаю огромное количество памяти, выделенной для .NET, которая указана как "свободная" профилировщиком ANTS. (Я подтвердил эту проблему другими профайлерами, такими как Mem Profiler и профилировщик CLR.

ANTS показывает, что у меня есть большая часть фрагментации памяти (100% свободной памяти с 150 МБ как самый большой кусок.) Общий размер всех объектов в куче составляет 180 МБ. У меня выделено 553 МБ для .NET и 152, выделенных для "Неуправляемых".

Однако размер большой кучи объектов (LOH) составляет всего 175 КБ. Это фактический размер выделенных там объектов. Я не выделяю никаких объектов, которые в конечном итоге оказываются на LOH.

Следовательно, моя проблема, некоторые, где по линии, я подозреваю, что я как бы распределяю большие объекты (над пределом 85k для LOH), а затем удаляю их.

Я читаю большие объемы данных (оценивая здесь в нескольких МБ) из баз данных (Oracle, Sql Server), копируя эти данные в массивы объектов в памяти и обрабатывая данные в индексы (массивы, словари и т.д.) для более простой поиск/фильтрация/обработка.

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

Что мне нужно - это профилировщик, который отслеживает каждый объект, выделенный на LOH, поэтому я могу выяснить, что вызывает фрагментацию LOH и чрезмерное использование памяти (память не возвращается OS, поэтому похоже, что мой процесс занимает 1 ГБ памяти для хранения 200 МБ выделенных объектов.) Я думаю, память не возвращается, потому что LOH не уплотняется, поэтому я застрял со всей этой памятью на всю жизнь моего процесса, который может быть недели (он работает как служба Windows.)

Изменить: Моя проблема в том, что мое приложение .NET использует большую память, которую я не могу отслеживать.

Edit1: Я использовал профилировщик памяти Visual Studio. Хотя он сообщает мне все объекты, которые создаются, сколько, общих байтов и т.д., Я не даю мне подсказки о том, почему у меня так много свободной памяти. Моя единственная подсказка/подсказка - это то, что говорит ANTS: "Фрагментация памяти ограничивает размер объектов, которые могут быть выделены". и у меня много неиспользуемой памяти, выделенной для .NET, которая не используется.

Edit2: Больше профилирования показывает, что у меня есть некоторые непродолжительные большие объекты, выделенные на LOH. Однако общая сумма, выделенная на LOH, не превышает 3-4 МБ. Однако за это время частные байты пробиваются через крышу, удваиваются и утроиваются, а размер моих фактически выделенных объектов (на всех кучах) только немного растет. Например, байты во всех кучах составляют 115 МБ, но мои личные байты составляют более 512 МБ.

ANTS ясно говорит мне, что у меня проблема с фрагментацией памяти. Оказывается, я создаю короткоживущие объекты на LOH. Однако эти объекты никогда не имеют более 3 или 4 МБ. Значит, эти непродолжительные крупные объекты (кажется,?) Фрагментируют черту из LOH.

Откликнуться на Эрика Липперта и аналогию парковки в Диснейленд (что здорово).

Как будто кто-то парки в пятне на несколько минут, а затем уходит. Затем это место зарезервировано (никто другой не может припарковаться там), пока я не спал с парковкой!

Сначала я начал исследовать это, когда Visual Studio предупредил меня об использовании памяти и рекомендовал перейти на x64. (Я забыл номер предупреждения, быстрый google не находит его). Таким образом, переход на x64 устраняет непосредственную проблему, но не устраняет основную проблему.

Мне нравится, что у меня есть парковка на 1000 автомобилей, но после того, как я положил в нее 100 автомобилей, мои автосигналисты кричат, что они полны...

К счастью, у меня огромный кластер VMware и разумный администратор. Мне было выделено 8 процессоров и 8 ГБ памяти. Что касается проблемы, я могу справиться с этим, я просто бросаю в нее ресурсы. Кроме того, (как я уже сказал выше), я переключился на x64 некоторое время назад, когда Visual Studio продолжала наталкивать меня на предупреждение. Однако я хотел бы выяснить, что он выделил на LOH, чтобы увидеть, уменьшаю ли я эту фрагментацию кучи с помощью некоторые небольшие изменения кода. Возможно, это безумное поручение, учитывая, что я могу просто выбросить на него ресурсы.

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

Edit3: http://msdn.microsoft.com/en-us/magazine/cc188781.aspx

ObjectAllocatedByClass не отслеживает выделение большого объекта куча, но ObjectAllocated. Сравнивая уведомления из двое, предприимчивая душа должна быть в состоянии выяснить, что находится в большая куча объекта в отличие от обычной управляемой кучи.

Итак, похоже, что это можно сделать. Тем не менее, мои навыки на С++ - это способ ржавого проникновения в это (возможно, когда-нибудь в будущем, если я получу больше времени). Я надеялся, что профилировщик предоставит это из коробки.

Ответы

Ответ 1

После экспериментов я смог получить уведомление, когда GC удаляет вещи из поколения, но не тогда, когда он ее там помещает.

Так как LOH не является специфичным для генерации, и нет специального события, к которому я могу получить доступ для уведомлений о вставках LOH, то единственной альтернативой, которую я могу предложить, является отладка приложения, получение аварийного дампа - ИЛИ - запуск его локально и использование WinDBG. Вот как вы можете это сделать:

  • Загрузите Windows SDK 7
  • Скопируйте% Microsoft_NET_Framework%\sos.dll в каталог WinDBG
  • В WinDBG Нажмите "Файл" → "Открыть исполняемый файл" → укажите на свой исполняемый файл
  • В командной строке внизу тип g (Go)
  • Наблюдайте за памятью, и когда вы хотите ее проанализировать, перейдите в меню WinDBG → Debug → Break
  • Введите load .sos - для загрузки расширений .NET
  • Тип !dumpheap -min 85000 - это список больших объектов, которые должны находиться на LOH
         Address               MT     Size
0000000012a17048 000007fee7ae6ae8   400032     
0000000012a78b00 000007fee7ae6ae8   400032     
0000000012ada5b8 000007fee7ae6ae8   400032     
0000000012b3c070 000007fee7ae6ae8   400032     
0000000012b9db28 000007fee7ae6ae8   400032

Далее нам нужно пройти каждый из них и узнать, что в них.

  • Скопируйте первый столбец (адрес объекта) в буфер обмена
  • Тип !do <paste from clipboard>
  • В этом списке будет указано содержимое объекта, его тип и размер.
CLR Version: 4.0.30319.261
SOS Version: 4.0.30319.239
Name:        System.String
MethodTable: 000007fee7ae6ae8
EEClass:     000007fee766ed68
Size:        400026(0x61a9a) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      8470737076787475867884758166807183888774746571677189..
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fee7aec9d0  4000103        8         System.Int32  1 instance           200000 m_stringLength
000007fee7aeb510  4000104        c          System.Char  1 instance               38 m_firstChar
000007fee7ae6ae8  4000105       10        System.String  0   shared           static Empty
                                 >> Domain:Value  000000000055fe50:0000000002a11420 <<

И строки, которые вы хотите найти:

Size:        400026(0x61a9a) bytes
String:      8470737076787475867884758166807183888774746571677189..
  • Сделайте это для каждого объекта

(Тем не менее, я предполагаю, что это будет строка, поэтому проверяйте свойство "Name", чтобы быть уверенным. Это может быть массив.)

Ответ 2

О боже - @EricLippert в вопросе - я знаю, что у меня это неправильно, но здесь идет: -)

LOH будет хранить только определенные объекты размером более 85 КБ. Я нашел по опыту, это обычно строки - XML ​​или большие перечислимые типы, которые были в глобальных переменных и/или статических переменных.

Если ваш LOH невелик, вы должны хранить другие объекты в памяти, которые, скорее всего, заканчиваются как поколение 2. Это могут быть глобальные переменные, такие как массивы, словари на протяжении всего жизненного цикла приложения.

Итак, прежде всего, чтобы быстро проверить, есть ли какие-либо переменные, которые будут присутствовать на протяжении всего срока службы приложения? У них есть тысячи записей?

Во-вторых,.NET имеет статистику счетчика производительности для размера памяти для каждого размера. Счетчики могут быть указаны непосредственно в приложении, поэтому это будет хорошим следующим шагом.

Наконец, GC будет очищать память только тогда, когда это необходимо. Так что да, возможно, он остался бы того же размера, если бы GC не почувствовал необходимость (давление в памяти), чтобы прояснить что-либо. Вы можете заставить сбор мусора, но обычно рекомендуется, чтобы GC выполнял свою работу.

Я задал аналогичный вопрос о почему строка хранилась в моем приложении так долго. Я использовал WinDBG для анализа аварийной дампы, которую я заставил, Диспетчер задач → Процессы → Щелкните правой кнопкой мыши → Создать аварийный дамп. Затем загрузили его в WinDBG (Windows SDK 7).

Надеемся, что ссылка предоставит вам больше рекомендаций для отслеживания того, где проблема коренится (каламбур).

И вопрос обратно к OP: это сильно влияет на ваш сервер или вам просто интересно, почему память хранится?