Понимание счетчиков производительности памяти - PullRequest
5 голосов
/ 21 сентября 2010

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

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

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

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

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

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

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

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


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

Привет!

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

Я прочитал отличную статью о LOH здесь -> http://www.simple -talk.com / dotnet /.net-framework / куча опасностей большого объекта /

Потрясающе читать!

А, http://dotnetdebug.net/2005/06/30/perfmon-your-debugging-buddy/

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

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

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

  2. Какой тип данных использовал максимальный объем памяти

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

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

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


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

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

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

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

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

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

СоветыЯ уже знаю

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

  2. Избегать создания объектов / массивов> 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 ГБ физической ОЗУ </li>
  • Увеличение размера виртуальной памяти (файла подкачки) в этом сценарии не влияет.

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

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

Ответы [ 3 ]

2 голосов
/ 22 сентября 2010

Наян, вот ответы на ваши вопросы и пара дополнительных советов.

  1. Вы не можете освободить их, вы можете только облегчить их сбор GC. Кажется, вы уже знаете путь: ключ сокращает количество ссылок на объект.
  2. Фрагментация - это еще одна вещь, которую вы не можете контролировать. Но есть несколько факторов, которые могут повлиять на это:
    • Внешняя фрагментация 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 намного более тяжелая. .

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

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

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

2 голосов
/ 21 сентября 2010

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

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

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

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

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

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

0 голосов
/ 14 апреля 2017

Другой индикатор наблюдает за Private Bytes против Bytes in all Heaps.Если Private Bytes увеличивается быстрее, чем Bytes in all Heaps, у вас есть неуправляемая утечка памяти.Если 'Bytes in all Heaps' увеличивается быстрее, чем 'Private Bytes', это управляемая утечка.

Чтобы исправить то, что @Alexey Nedilko сказал:

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...