Понимание счетчиков производительности памяти

[Обновление - 30 сентября 2010 г.]

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

1) Используйте профилировщик памяти (для начала попробуйте CLR Profiler) и найдите подпрограммы, которые используют max mem, и настройте их, например, повторно используйте большие массивы, постарайтесь свести к минимуму ссылки на объекты.

2) Если возможно, выделите небольшие объекты (менее 85 КБ для .NET 2.0) и используйте пулы памяти, если вы можете избежать высокой загрузки ЦП сборщиком мусора.

3) Если вы увеличиваете количество ссылок на объекты, вы обязаны разыменовывать их одинаковое количество раз. Вы будете спокойны, и код, вероятно, будет работать лучше.

4) Если ничего не работает, а вы все еще не понимаете, используйте метод исключения (комментарий / пропуск кода), чтобы узнать, что потребляет больше всего памяти.

Использование счетчиков производительности памяти внутри вашего кода также может вам помочь.

Надеюсь, что это поможет!

[Оригинальный вопрос]

Привет!

Я работаю в C #, и моя проблема не связана с памятью.

Я прочитал отличную статью о LOH здесь ->http://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/

Классно читать!

А также,http://dotnetdebug.net/2005/06/30/perfmon-your-debugging-buddy/

Моя проблема:

Я сталкиваюсь с проблемой нехватки памяти в настольном приложении уровня предприятия. Я пытался читать и понимать вещи о профилировании памяти и счетчике производительности (пробовал также WinDBG! - немного), но все еще не в курсе основных вещей.

Я попытался CLR Profiler для анализа использования памяти. Это было полезно в:

Показывая мне, кто выделил огромные куски памяти

Какой тип данных используется максимальная память

Но как CLR Profiler, так и счетчики производительности (поскольку они используют одни и те же данные) не смогли объяснить:

Числа, которые собираются после каждого запуска приложения - как понять, есть ли улучшения?!?!

Как сравнивать данные о производительности после каждого прогона - хорошо или плохо меньше или больше число конкретного счетчика?

Что мне нужно:

Я ищу советы по:

Как освободить (да, верно)удалось объекты типа данных (например, массивы, большие строки) - но не путем вызовов GC.Collect, если это возможно. Я должен обрабатывать массивы байтов длиной около 500 КБ (неизбежный размер :-() время от времени.

Если происходит фрагментация, как сжимать память - так как кажется, что .NET GC не очень эффективно делает это и вызывает OOM.

Кроме того, что конкретно является лимитом 85 КБ для LOH? Это размер объекта общего размера массива? Это не очень понятно для меня.

Какие счетчики памяти могут определить, действительно ли изменения кода снижают шансы OOM?

Советы, которые я уже знаю

Установите для управляемых объектов значение null - пометьте их как мусор, чтобы сборщик мусора мог их собрать. Это странно - после установкиСтрока [] объект к нулю,# байт во всех кучах выстрелил

Избегайте создания объектов / массивов> 85 КБ - это не в моем контроле. Таким образом, может быть много LOH.

3.

Memory Leaks Indicators:

# bytes in all Heaps increasing
Gen 2 Heap Size increasing
# GC handles increasing
# of Pinned Objects increasing
# total committed Bytes increasing
# total reserved Bytes increasing
Large Object Heap increasing

Моя ситуация:

У меня есть 4 ГБ, 32-битный компьютер с Wink 2K3 сервер SP2 на нем.Я понимаю, что приложение может использовать <= 2 ГБ физической памятиУвеличение размера виртуальной памяти (файла подкачки) в этом сценарии не влияет.

В связи с проблемой OOM я сосредоточился только на счетчиках, связанных с памятью.

Пожалуйста посоветуй!Мне действительно нужна помощь, потому что я застрял из-за отсутствия хорошей документации!

 Nayan21 сент. 2010 г., 15:22
Спасибо, Хэнк, за помощь. С уважением.
 Nayan21 сент. 2010 г., 14:11
Я понимаю, Хенк. Но я не могу предоставить код. Кроме того, я ищу только «рекомендации», чтобы уменьшить нагрузку на память. Если вы можете помочь, пожалуйста! Спасибо! :)

Ответы на вопрос(3)

оятельно. Например, если вам часто нужны массивы <500 тыс., А количество живых массивов за раз хорошо известно, вы можете избежать их освобождения никогда - таким образом, если вам нужно, скажем, 10 из них за раз, вы можете получить исправлены 5 МБ памяти вместо проблемной длительной фрагментации.

Что касается ваших трех вопросов:

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

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

Это размер объекта, а не количество элементов в массиве.

Помните, фрагментация происходит только тогда, когда объекты освобождены, а не когда они выделены. Если фрагментация действительно является вашей проблемой, повторное использование больших объектов поможет. Сосредоточьтесь на создании меньшего количества мусора (особенно большого мусора) в течение жизненного цикла приложения вместо того, чтобы пытаться разобраться с основными моментами реализации gc напрямую.

 Hans Passant21 сент. 2010 г., 11:41
«Величина и размер» - это, конечно, корень проблемы. Единственное волшебство, доступное здесь, - это 64-битная операционная система. Двести баксов, чтобы решить вашу проблему.
 Nayan21 сент. 2010 г., 10:40
Кроме того, если вы не используете неуправляемый код, как вы можете использовать пул памяти в управляемом коде? Любое хорошее чтение?
 Nayan21 сент. 2010 г., 10:38
Хорошая идея, но понимаю, что это приложение уровня предприятия. Пожалуйста, поймите, размер и размер приложения. Следовательно, я не могу пойти на такие огромные изменения дизайна. Есть еще идеи по оптимизации? :)
 Nayan21 сент. 2010 г., 12:40
лол .. хороший совет Ганс! :) Но я сомневаюсь, что моя компания согласится на это. Это нормальная проблема с такими широко популярными, плохо разработанными корпоративными продуктами = D
 Nayan21 сент. 2010 г., 23:00
Хорошо, Blucz, а у тебя есть хорошие примеры для реализации таких вещей? Мол, управляемый пул памяти? Любой хороший документ? Спасибо!
 blucz21 сент. 2010 г., 19:57
Когда я предложил вам объединять и управлять вашими объектами, я предлагал вам повторно использовать управляемые объекты ... вам не нужно привлекать неуправляемый код для зависания списка массивов и их повторного использования.

Private Bytes противBytes in all Heaps, ЕслиPrivate Bytes увеличивается быстрее, чемBytes in all Heaps, у вас есть неуправляемая утечка памяти. Если «Bytes in all Heaps» увеличивается быстрее, чем «Private Bytes», это управляемая утечка.

Чтобы исправить то, что @ Алексей Недилько сказал:

«Внешняя фрагментация LOH менее опасна, чем внешняя фрагментация Gen2, потому что LOH не уплотнен. Вместо этого можно повторно использовать свободные слоты LOH».

абсолютно неверно, Gen2 сжат, что означает, что после коллекции никогда не остается свободного места. LOH НЕ сжат (как он правильно упоминает), и да, свободные слоты используются повторно.НО если свободного места нетсмежный чтобы соответствовать запрошенному распределению, тогда размер сегмента увеличивается - и может продолжать расти и расти, Таким образом, в LOH могут появиться пробелы, которые никогда не заполняются. Это частая причина OOM, и я видел это во многих дампах памяти, которые я анализировал.

Хотя в GC API теперь есть методы (начиная с .NET 4.51), которые можно вызывать для программного уплотнения LOH, я настоятельно рекомендую избегать этого - если производительность приложения является проблемой. Выполнение этой операции во время выполнения чрезвычайно дорого и значительно снижает производительность вашего приложения. Причина, по которой реализация GC по умолчанию должна была быть эффективной, поэтому они пропустили этот шаг в первую очередь. IMO, если вы обнаружите, что вам нужно вызывать это из-за фрагментации LOH, вы делаете что-то не так в своем приложении - и это можно улучшить с помощью методов объединения, разделения массивов и других приемов выделения памяти. Если это приложение является автономным приложением или каким-то пакетным процессом, в котором производительность не имеет большого значения, возможно, это не так уж и плохо, но я бы использовал его в лучшем случае экономно.

Хороший наглядный пример того, как это может произойти, здесь -Опасности кучи больших объектов и здесьОбнаружена куча больших объектов - Маони (руководитель группы GC в CLR)

Решение Вопроса

Вы не можете освободить их, вы можете только облегчить их сбор GC. Кажется, вы уже знаете путь: ключ сокращает количество ссылок на объект.Фрагментация - это еще одна вещь, которую вы не можете контролировать. Но есть несколько факторов, которые могут повлиять на это:Внешняя фрагментация LOH менее опасна, чем внешняя фрагментация Gen2, потому что LOH не уплотнен. Вместо этого можно повторно использовать свободные слоты LOH.Если байтовые массивы 500 КБ, на которые ссылаются, используются в качестве некоторых буферов ввода-вывода (например, передаются в некоторый API-интерфейс на основе сокетов или неуправляемый код), высока вероятность того, что они будут закреплены. Прикрепленный объект не может быть сжат GC, и они являются одной из наиболее частых причин фрагментации кучи.85K - это ограничение на размер объекта. Но помните, экземпляр System.Array тоже является объектом, поэтому все ваши 500K байт [] находятся в LOH.Все счетчики, которые есть в вашем посте, могут дать подсказку об изменениях потребления памяти, но в вашем случае я бы выбрал BIAH (байт во всех кучах) и размер LOH в качестве основных индикаторов. BIAH показывает общий размер всех управляемых куч (точнее, Gen1 + Gen2 + LOH, без Gen0 - но кого волнует Gen0, верно? :)), а LOH - куча, в которой размещаются все большие байты [].

Советы:

Что-то, что уже было предложено: предварительно выделить и объединить ваши буферы.

Другой подход, который может быть эффективным, если вы можете использовать любую коллекцию вместо непрерывного массива байтов (это не тот случай, если буферы используются в IO): реализовать собственную коллекцию, которая внутренне будет состоять из множества массивов меньшего размера. Это похоже на std :: deque из библиотеки C ++ STL. Поскольку каждый отдельный массив будет меньше 85 КБ, вся коллекция не попадет в LOH. Преимущество, которое вы можете получить с этим подходом, заключается в следующем: LOH собирается только тогда, когда происходит полный сбор данных. Если байт [] в вашем приложении не является долгоживущим и (если бы он был меньше по размеру) попадал в Gen0 или Gen1 перед сборкой, это значительно упростило бы управление памятью для GC, поскольку сборка Gen2 намного более тяжелая. ,

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

Я также рекомендовал бы взглянуть на% Time в счетчике GC, так как это может быть хорошим индикатором эффективности управления памятью. Чем больше это значение, тем больше времени ваше приложение тратит на процедуры GC вместо обработки запросов от пользователей или выполнения других «полезных» операций. Я не могу дать совет относительно того, какие абсолютные значения этого счетчика указывают на проблему, но я могу поделиться своим опытом для вашей справки: для приложения, над которым я работаю, мы обычно рассматриваем% Time в GC выше 20% как проблему.

Кроме того, было бы полезно, если бы вы поделились некоторыми значениями счетчиков перфорации, связанных с памятью вашего приложения: частные байты и рабочий набор процесса, BIAH, общее количество принятых байтов, размер LOH, Gen0, Gen1, размер Gen2, # of Gen0, Gen1, Gen2 коллекции,% времени в GC. Это поможет лучше понять вашу проблему.

 Nayan22 сент. 2010 г., 04:49
Пожалуйста, очистите ... вы сказали: «Свободные слоты LOH можно использовать повторно» - GC или программистом?
 Nayan22 сент. 2010 г., 12:37
Еще раз большое спасибо!
 Nayan22 сент. 2010 г., 04:51
Можете ли вы ответить на это (это озадачивает меня) - больше число BIAH после каждого теста лучше или хуже?
 Nayan22 сент. 2010 г., 04:46
Алексей, ты действительно помог мне! Это один из тех редких моментов, когда кто-то честно отвечал и пытался помочь, а не проповедовать излишне. Большое спасибо за советы. И я вернусь с некоторой контр-статистикой, чтобы поделиться точным характером проблемы! С уважением!
 Alexey Nedilko22 сент. 2010 г., 08:45
- Что касается размера BIAH, то нет общего ответа на вопрос, является ли чем меньше размер, тем лучше (у каждого приложения есть свой собственный шаблон использования памяти), но в вашем случае условий OOM вашими задачами должны быть небольшие кучи (и, следовательно, меньший размер BIAH).
 Alexey Nedilko22 сент. 2010 г., 08:41
- Это GC, который может повторно использовать свободное пространство внутри LOH для выделения нового объекта. Еще немного лох чтения:msdn.microsoft.com/en-us/magazine/cc534993.aspx

Ваш ответ на вопрос