Поделиться через


Сборка мусора и производительность

В этой статье описываются проблемы, связанные с сбором мусора и использованием памяти. В нем рассматриваются проблемы, относящиеся к управляемой куче, и объясняется, как свести к минимуму влияние сборки мусора на приложения. Каждая проблема содержит ссылки на процедуры, которые можно использовать для изучения проблем.

Средства анализа производительности

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

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

Счетчики производительности можно использовать для сбора данных о производительности. Инструкции см. в разделе "Профилирование среды выполнения". Категория счетчиков производительности памяти .NET CLR, как описано в счетчиках производительности в .NET, предоставляет информацию о сборщике мусора.

Отладка с помощью SOS

Вы можете использовать отладчик Windows (WinDbg) для проверки объектов в управляемой куче.

Чтобы установить WinDbg, установите средства отладки для Windows на странице "Скачать средства отладки для Windows ".

События трассировки мусора

Трассировка событий для Windows (ETW) — это система трассировки, которая дополняет профилирование и отладку, предоставляемую .NET. Начиная с .NET Framework 4, события ETW для сборки мусора собирают полезные сведения для анализа управляемой кучи со статистической точки зрения. Например, событие, GCStart_V1 которое возникает при сборке мусора, предоставляет следующие сведения:

  • Какое поколение объектов собирается.
  • Что активировало сборку мусора.
  • Тип сборки мусора (параллельная или не одновременная).

Ведение журнала событий ETW эффективно и не будет скрывать проблемы с производительностью, связанные со сборкой мусора. Процесс может предоставлять собственные события в сочетании с событиями ETW. При регистрации события приложения и события сборки мусора можно сопоставить, чтобы определить, как и когда возникают проблемы с кучей. Например, серверное приложение может предоставлять события в начале и конце клиентского запроса.

API профилирования

Интерфейсы профилирования среды CLR предоставляют подробные сведения об объектах, затронутых во время сборки мусора. Профилировщик может получать уведомления о начале и завершении сборки мусора. Он может предоставлять отчеты об объектах в управляемой куче с идентификацией объектов в каждой генерации. Дополнительные сведения см. в разделе "Обзор профилирования".

Профилировщики могут предоставлять исчерпывающую информацию. Однако сложные профилировщики могут потенциально изменить поведение приложения.

Мониторинг ресурсов домена приложения

Начиная с .NET Framework 4, мониторинг ресурсов домена приложения (ARM) позволяет узлам отслеживать использование ЦП и памяти по домену приложения. Дополнительные сведения см. в разделе "Мониторинг ресурсов домена приложения".

Устранение неполадок с производительностью

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

Проблема: выдается исключение из-за нехватки памяти

Существует два законных случая для создания управляемого:OutOfMemoryException

  • Не хватает виртуальной памяти.

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

  • Недостаточно физической памяти для выделения.

Проверки производительности
Определите, обрабатывается ли исключение недостатка памяти.
Определите, сколько виртуальной памяти можно резервировать.
Определите, достаточно ли физической памяти.

Если вы определите, что исключение не является законным, обратитесь в службу поддержки клиентов Майкрософт со следующими сведениями:

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

Проблема. Процесс использует слишком много памяти

Распространенное предположение заключается в том, что отображение использования памяти на вкладке "Производительность " диспетчера задач Windows может указывать, когда используется слишком много памяти. Однако это отображение относится к рабочему набору; Он не предоставляет сведения об использовании виртуальной памяти.

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

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

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

Проблема: сборщик мусора не освобождает объекты достаточно быстро

Когда кажется, что объекты не собираются для утилизации так, как ожидалось, необходимо определить, есть ли на эти объекты сильные ссылки.

Вы также можете столкнуться с этой проблемой, если для поколения, содержащего мертвый объект, не была проведена сборка мусора, что означает, что финализатор для мертвого объекта не был запущен. Например, это возможно, если вы используете приложение с однопоточной моделью (STA), и поток, который обслуживает очередь завершителей, не может обратиться к нему.

Проверки производительности
Проверьте ссылки на объекты.
Проверьте, был ли запущен финализатор.
Определите, есть ли объекты, ожидающие завершения.

Проблема: управляемая куча слишком фрагментирована

Уровень фрагментации вычисляется как отношение свободного пространства к общему объему выделенной памяти для поколения. Для поколения 2 допустимый уровень фрагментации составляет не более 20%. Поскольку поколение 2 может становиться очень большим, уровень фрагментации более важен, чем абсолютное значение.

Наличие большого количества свободного места в поколении 0 не является проблемой, так как это поколение, в котором выделяются новые объекты.

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

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

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

Если фрагментация виртуальной памяти не позволяет сборщику мусора добавлять сегменты, причины могут быть одним из следующих:

  • Частая загрузка и выгрузка множества небольших узлов.

  • При взаимодействии с неуправляемыми кодами слишком много ссылок на COM-объекты.

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

    При размещении среды CLR приложение может запросить, чтобы сборщик мусора сохранил свои сегменты. Это уменьшает частоту выделения сегментов. Это достигается с помощью флага STARTUP_HOARD_GC_VM в перечислении STARTUP_FLAGS.

Проверки производительности
Установите объем свободного пространства в управляемой куче.
Определите количество закрепленных объектов.

Если вы считаете, что нет законной причины фрагментации, обратитесь в службу поддержки Майкрософт.

Проблема: паузы сборки мусора слишком длительны

Сборка мусора работает в мягком режиме реального времени, поэтому приложение должно иметь возможность терпеть некоторые паузы. Критерий мягкого реального времени заключается в том, что 95% операций должны завершиться вовремя.

В сборке мусора с параллельной обработкой управляемые потоки получают возможность выполнения во время процесса сбора мусора, что означает, что приостановки очень небольшие.

Эфемерные сборки мусора (поколения 0 и 1) продолжаются всего несколько миллисекунд, поэтому снижение задержек обычно нецелесообразно. Однако можно уменьшить паузы в коллекциях поколения 2, если приложение изменит шаблон запросов на выделение памяти.

Другой, более точный метод заключается в использовании событий ETW сборки мусора. Вы можете определить время сбора, суммируя разницу временных меток для последовательности событий. Вся последовательность сбора включает приостановку исполняющего механизма, сам сбор мусора и возобновление исполняющего механизма.

Вы можете использовать уведомления о сборке мусора , чтобы определить, должен ли сервер иметь коллекцию поколения 2 и может ли перенаправление запросов на другой сервер облегчить любые проблемы с приостановками.

Проверки производительности
Определите продолжительность времени в сборке мусора.
Определите причину сборки мусора.

Проблема: поколение 0 слишком большое

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

Проблема. Загрузка ЦП во время сборки мусора слишком высока

Загрузка ЦП будет высокой во время сборки мусора. Если значительное время процесса тратится на сборку мусора, количество сборок слишком велико или сборка длится слишком долго. Увеличение скорости распределения объектов в управляемой куче приводит к увеличению частоты сборки мусора. Уменьшение скорости распределения снижает частоту сборок мусора.

Вы можете отслеживать показатели распределения с помощью счетчика Allocated Bytes/second производительности. Дополнительные сведения см. в разделе "Счетчики производительности" в .NET.

Длительность коллекции в первую очередь зависит от числа объектов, которые выживают после распределения. Сборщик мусора должен обработать большой объем памяти, если многие объекты остаются для сбора. Работа по обработке выживших требует много времени. Чтобы определить, сколько объектов было обработано во время сборки мусора, установите точку останова в отладчике в конце сборки мусора для конкретной генерации.

Проверки производительности
Определите, вызвана ли высокая загрузка ЦП сборкой мусора.
Установите точку останова в конце сборки мусора.

Рекомендации по устранению неполадок

В этом разделе описаны рекомендации, которые следует учитывать при начале расследования.

Сборка мусора рабочей станции или сервера

Определите, используете ли вы правильный тип сборки мусора. Если приложение использует несколько потоков и экземпляров объектов, используйте сборку мусора сервера вместо сборки мусора рабочей станции. Серверная сборка мусора работает с несколькими потоками, в то время как сборка мусора рабочей станции требует, чтобы несколько экземпляров приложения запускали свои собственные потоки сборки мусора и соревновались за время ЦП.

Приложение, которое имеет низкую нагрузку и выполняет задачи редко в фоновом режиме, например служба, может использовать сборку мусора рабочей станции с отключенной параллельной сборкой мусора.

Когда следует измерять размер управляемой кучи

Если вы не используете профилировщик, необходимо установить согласованный шаблон измерения для эффективной диагностики проблем с производительностью. Рассмотрим следующие моменты, чтобы установить расписание:

  • Если вы измеряете после сборки мусора поколения 2, вся управляемая куча будет свободна от мусора (мертвых объектов).
  • Если вы измеряете сразу после сборки мусора поколения 0, объекты в поколениях 1 и 2 еще не будут собраны.
  • Если вы измеряете непосредственно перед сборкой мусора, вы сможете измерить как можно большее количество выделенной памяти до начала сборки мусора.
  • Измерение во время сборки мусора проблематично, так как структуры данных сборщика мусора не находятся в допустимом состоянии для обхода и могут не дать полные результаты. Это сделано намеренно.
  • При использовании сборки мусора рабочей станции с одновременной сборкой мусора освобожденные объекты не компактизируются, поэтому размер кучи может быть одинаковым или больше (фрагментация может сделать его визуально больше).
  • Параллельная сборка мусора поколения 2 задерживается, когда нагрузка физической памяти слишком высока.

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

Установить точку останова в конце сборки мусора

  • В WinDbg с загруженным расширением отладчика SOS введите следующую команду:

    bp mscorwks!WKS::GCHeap::RestartEE "j (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) 'kb';'g'"

    Установите GcCondemnedGeneration на требуемое поколение. Для этой команды требуются закрытые символы.

    Эта команда вызывает разрыв, если RestartEE выполняется после освобождения объектов поколения 2 для сборки мусора.

    В серверной сборке мусора вызывается только один поток RestartEE, поэтому точка останова произойдет только один раз во время сборки мусора поколения 2.

Процедуры проверки производительности

В этом разделе описаны следующие процедуры, чтобы изолировать причину проблемы с производительностью:

Чтобы определить, вызвана ли проблема сборкой мусора

  • Проверьте следующие два счетчика производительности памяти:

    • % время в GC. Отображает процент истекшего времени, затраченного на выполнение сборки мусора после последнего цикла сборки мусора. Используйте этот счетчик, чтобы определить, тратится ли сборщик мусора слишком много времени, чтобы сделать управляемое пространство кучи доступным. Если время, затраченное на сборку мусора, относительно низко, это может указывать на проблему ресурсов за пределами управляемой кучи. Этот счетчик может быть не точным, если используется параллельная или фоновая сборка мусора.

    • # Всего зарезервированных байтов. Отображает объем виртуальной памяти, выделенной сборщиком мусора. Используйте этот счетчик, чтобы определить, является ли память, используемая сборщиком мусора, чрезмерной долей памяти, которую использует ваше приложение.

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

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

  1. В отладчике WinDbg или Visual Studio с загруженным расширением отладчика SOS, введите команду вывода исключения (pe) :

    !pe

    Если исключение управляемо, OutOfMemoryException отображается как тип исключения, как показано в следующем примере.

    Exception object: 39594518
    Exception type: System.OutOfMemoryException
    Message: <none>
    InnerException: <none>
    StackTrace (generated):
    
  2. Если выходные данные не указывают исключение, необходимо определить, из какого потока произошло исключение нехватки памяти. Введите следующую команду в отладчике, чтобы отобразить все потоки со своими стеками вызовов:

    ~\*kb

    Поток, содержащий стек с вызовами исключений, обозначается аргументом RaiseTheException. Это управляемый объект исключения.

    28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
    
  3. Для выгрузки вложенных исключений можно использовать эти команды.

    !pe -nested

    Если вы не найдете никаких исключений, исключение вне памяти возникло из неуправляемого кода.

Определение объема виртуальной памяти, который можно зарезервировать

  • В WinDbg с загруженным расширением отладчика SOS введите следующую команду, чтобы получить самый большой бесплатный регион:

    !address -summary

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

    Largest free region: Base 54000000 - Size 0003A980
    

    В этом примере размер крупнейшего свободного региона составляет примерно 24000 КБ (3A980 в шестнадцатеричном). Этот регион гораздо меньше, чем то, что требуется сборщику мусора для сегмента.

    –или–

  • Используйте команду vmstat:

    !vmstat

    Самый большой свободный регион — это наибольшее значение в столбце MAXIMUM, как показано в следующих выходных данных.

    TYPE        MINIMUM   MAXIMUM     AVERAGE   BLK COUNT   TOTAL
    ~~~~        ~~~~~~~   ~~~~~~~     ~~~~~~~   ~~~~~~~~~~  ~~~~
    Free:
    Small       8K        64K         46K       36          1,671K
    Medium      80K       864K        349K      3           1,047K
    Large       1,384K    1,278,848K  151,834K  12          1,822,015K
    Summary     8K        1,278,848K  35,779K   51          1,824,735K
    

Определение наличия достаточной физической памяти

  1. Запустите диспетчер задач Windows.

  2. На вкладке Performance обратите внимание на зафиксированное значение. (В Windows 7 посмотрите на Commit (KB) в System group.)

    Если Total близко к Limit, ваша система испытывает нехватку физической памяти.

Для определения объема памяти, зарезервированной управляемой кучей

  • # Total committed bytes Используйте счётчик производительности памяти, чтобы получить количество байтов, которые управляемая куча выделяет. Сборщик мусора закрепляет блоки в сегменте по мере необходимости, а не все одновременно.

    Замечание

    Не используйте счетчик # Bytes in all Heaps производительности, так как он не отражает фактическое использование памяти управляемой кучей. Размер поколения включается в это значение и на самом деле является его пороговым размером, то есть размером, вызывающим сборку мусора, если поколение заполнено объектами. Поэтому обычно это значение равно нулю.

Определение объема памяти, резервируемой управляемой кучей

  • # Total reserved bytes Используйте счетчик производительности памяти.

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

    Это важно

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

  • В отладчике WinDbg или Visual Studio с загруженным расширением отладчика SOS введите следующую команду:

    !eeheap -gc

    Результат выглядит следующим образом.

    Number of GC Heaps: 2
    ------------------------------
    Heap 0 (002db550)
    generation 0 starts at 0x02abe29c
    generation 1 starts at 0x02abdd08
    generation 2 starts at 0x02ab0038
    ephemeral segment allocation context: none
      segment    begin allocated     size
    02ab0000 02ab0038  02aceff4 0x0001efbc(126908)
    Large object heap starts at 0x0aab0038
      segment    begin allocated     size
    0aab0000 0aab0038  0aab2278 0x00002240(8768)
    Heap Size   0x211fc(135676)
    ------------------------------
    Heap 1 (002dc958)
    generation 0 starts at 0x06ab1bd8
    generation 1 starts at 0x06ab1bcc
    generation 2 starts at 0x06ab0038
    ephemeral segment allocation context: none
      segment    begin allocated     size
    06ab0000 06ab0038  06ab3be4 0x00003bac(15276)
    Large object heap starts at 0x0cab0038
      segment    begin allocated     size
    0cab0000 0cab0038  0cab0048 0x00000010(16)
    Heap Size    0x3bbc(15292)
    ------------------------------
    GC Heap Size   0x24db8(150968)
    

    Адреса, указанные "сегментом", являются начальными адресами сегментов.

Определение больших объектов в поколении 2

  • В отладчике WinDbg или Visual Studio с загруженным расширением отладчика SOS введите следующую команду:

    !dumpheap –stat

    Если управляемая куча большая, dumpheap может потребовать некоторое время.

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

    2c6108d4   173712     14591808 DevExpress.XtraGrid.Views.Grid.ViewInfo.GridCellInfo
    00155f80      533     15216804      Free
    7a747c78   791070     15821400 System.Collections.Specialized.ListDictionary+DictionaryNode
    7a747bac   700930     19626040 System.Collections.Specialized.ListDictionary
    2c64e36c    78644     20762016 DevExpress.XtraEditors.ViewInfo.TextEditViewInfo
    79124228   121143     29064120 System.Object[]
    035f0ee4    81626     35588936 Toolkit.TlkOrder
    00fcae40     6193     44911636 WaveBasedStrategy.Tick_Snap[]
    791242ec    40182     90664128 System.Collections.Hashtable+bucket[]
    790fa3e0  3154024    137881448 System.String
    Total 8454945 objects
    

    Последний объект, указанный в списке, является строкой и занимает больше всего места. Вы можете изучить приложение, чтобы узнать, как можно оптимизировать объекты строки. Чтобы просмотреть строки в диапазоне от 150 до 200 байт, введите следующее:

    !dumpheap -type System.String -min 150 -max 200

    Ниже приведен пример результатов.

    Address  MT           Size  Gen
    1875d2c0 790fa3e0      152    2 System.String HighlightNullStyle_Blotter_PendingOrder-11_Blotter_PendingOrder-11
    …
    

    Использование целого числа вместо строки для идентификатора может быть более эффективным. Если одна и та же строка повторяется тысячами раз, рассмотрите возможность интернинга строк. Дополнительные сведения о интернинге строк см. в справочном String.Intern разделе для метода.

Определение ссылок на объекты

  • В WinDbg с загруженным расширением отладчика SOS введите следующую команду, чтобы получить список ссылок на объекты:

    !gcroot

    –или–

  • Чтобы определить ссылки для определенного объекта, добавьте адрес:

    !gcroot 1c37b2ac

    Корни, найденные на стеках, могут быть ложноположительными результатами. Чтобы получить дополнительные сведения, используйте команду !help gcroot.

    ebx:Root:19011c5c(System.Windows.Forms.Application+ThreadContext)->
    19010b78(DemoApp.FormDemoApp)->
    19011158(System.Windows.Forms.PropertyStore)->
    … [omitted]
    1c3745ec(System.Data.DataTable)->
    1c3747a8(System.Data.DataColumnCollection)->
    1c3747f8(System.Collections.Hashtable)->
    1c376590(System.Collections.Hashtable+bucket[])->
    1c376c98(System.Data.DataColumn)->
    1c37b270(System.Data.Common.DoubleStorage)->
    1c37b2ac(System.Double[])
    Scan Thread 0 OSTHread 99c
    Scan Thread 6 OSTHread 484
    

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

Определить, был ли запущен финализатор

  • Запустите тестовую программу, содержащую следующий код:

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    

    Если тест устраняет проблему, это означает, что сборщик мусора не освобождал объекты, так как финализаторы для этих объектов были приостановлены. Метод GC.WaitForPendingFinalizers позволяет финализаторам завершить свои задачи и устраняет проблему.

Определение наличия объектов, ожидающих завершения

  1. В отладчике WinDbg или Visual Studio с загруженным расширением отладчика SOS введите следующую команду:

    !finalizequeue

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

  2. Чтобы получить выходные данные потоков, введите следующую команду:

    !threads -special

    Эта команда предоставляет выходные данные, например следующие.

       OSID     Special thread type
    2    cd0    DbgHelper
    3    c18    Finalizer
    4    df0    GC SuspendEE
    

    Поток завершения указывает, какой метод завершения, если таковой имеется, в данный момент выполняется. Если поток финализатора не выполняет методов финализации, он ожидает события, которое даст ему сигнал для выполнения своей работы. Большую часть времени вы увидите поток финализатора в этом состоянии, так как он работает с THREAD_HIGHEST_PRIORITY и должен очень быстро завершить выполнение финализаторов, если они существуют.

Чтобы определить количество свободного места в управляемой куче

  • В отладчике WinDbg или Visual Studio с загруженным расширением отладчика SOS введите следующую команду:

    !dumpheap -type Free -stat

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

    total 230 objects
    Statistics:
          MT    Count    TotalSize Class Name
    00152b18      230     40958584      Free
    Total 230 objects
    
  • Чтобы определить свободное пространство в поколении 0, введите следующую команду для сведений о потреблении памяти по поколению:

    !eeheap -gc

    Эта команда отображает выходные данные, аналогичные приведенному ниже. Последняя строка показывает эфемерный сегмент.

    Heap 0 (0015ad08)
    generation 0 starts at 0x49521f8c
    generation 1 starts at 0x494d7f64
    generation 2 starts at 0x007f0038
    ephemeral segment allocation context: none
    segment  begin     allocated  size
    00178250 7a80d84c  7a82f1cc   0x00021980(137600)
    00161918 78c50e40  78c7056c   0x0001f72c(128812)
    007f0000 007f0038  047eed28   0x03ffecf0(67103984)
    3a120000 3a120038  3a3e84f8   0x002c84c0(2917568)
    46120000 46120038  49e05d04   0x03ce5ccc(63855820)
    
  • Вычислите пространство, используемое поколением 0:

    ? 49e05d04-0x49521f8c

    Результат выглядит следующим образом. Поколение 0 составляет около 9 МБ.

    Evaluate expression: 9321848 = 008e3d78
    
  • Следующая команда сбрасывает свободное пространство в диапазоне поколения 0:

    !dumpheap -type Free -stat 0x49521f8c 49e05d04

    Результат выглядит следующим образом.

    ------------------------------
    Heap 0
    total 409 objects
    ------------------------------
    Heap 1
    total 0 objects
    ------------------------------
    Heap 2
    total 0 objects
    ------------------------------
    Heap 3
    total 0 objects
    ------------------------------
    total 409 objects
    Statistics:
          MT    Count TotalSize Class Name
    0015a498      409   7296540      Free
    Total 409 objects
    

    В этом выводе показано, что область кучи поколения 0 использует 9 МБ пространства для объектов и имеет свободные 7 МБ. Этот анализ показывает степень, в которой поколение 0 способствует фрагментации. Это количество использования кучи должно быть вычтено из общего объема, поскольку являет причиной фрагментации долгосрочными объектами.

Определение количества закрепленных объектов

  • В отладчике WinDbg или Visual Studio с загруженным расширением отладчика SOS введите следующую команду:

    !gchandles

    Статистика, отображаемая, включает количество закрепленных дескрипторов, как показано в следующем примере.

    GC Handle Statistics:
    Strong Handles:      29
    Pinned Handles:      10
    

Определение длительности процесса сборки мусора

  • % Time in GC Проверьте счетчик производительности памяти.

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

    Время сбора получается путем умножения интервала выборки с процентным значением.

    Следующие данные показывают четыре интервала выборки в два секунды для 8-секундного исследования. Столбцы Gen0, Gen1 и Gen2 показывают общее количество сборок мусора, завершенных к концу интервала для данного поколения.

    Interval    Gen0    Gen1    Gen2    % Time in GC
            1       9       3       1              10
            2      10       3       1               1
            3      11       3       1               3
            4      11       3       1               3
    

    Эта информация не показывает, когда произошла сборка мусора, но можно определить количество сборок мусора, которые произошли в течение интервала времени. Предположим, что в худшем случае десятое поколение 0 сборки мусора закончилось в начале второго интервала, а одиннадцатое поколение 0 сборки мусора закончилось в конце третьего интервала. Время между окончанием десятой и одиннадцатой сборок мусора составляет около 2 секунд, а счетчик производительности показывает 3%, так что продолжительность сборки мусора поколения 0 по окончании одиннадцатой сборки составила (2 секунды * 3% = 60 мс).

    В следующем примере существует пять интервалов.

    Interval    Gen0    Gen1    Gen2     % Time in GC
            1       9       3       1                3
            2      10       3       1                1
            3      11       4       1                1
            4      11       4       1                1
            5      11       4       2               20
    

    Сборка мусора второго поколения началась в течение четвертого интервала и закончилась в течение пятого интервала. Предполагая худший случай, последняя сборка мусора была для коллекции поколения 0, которая закончилась в начале третьего интервала, и сборка мусора поколения 2 закончилась в конце пятого интервала. Поэтому время между окончанием сборки мусора поколения 0 и окончанием сборки мусора поколения 2 составляет 4 секунды. Поскольку счетчик % Time in GC равен 20%, максимальное время, которое могла бы занять сборка мусора 2-го поколения, составляет 4 секунды * 20% = 800 мс.

  • Кроме того, можно определить длину сборки мусора с помощью событий трассировки мусора и проанализировать информацию, чтобы определить длительность сборки мусора.

    Например, в следующих данных показана последовательность событий, которая произошла во время не одновременной сборки мусора.

    Timestamp    Event name
    513052        GCSuspendEEBegin_V1
    513078        GCSuspendEEEnd
    513090        GCStart_V1
    517890        GCEnd_V1
    517894        GCHeapStats
    517897        GCRestartEEBegin
    517918        GCRestartEEEnd
    

    Время приостановки управляемого потока составило 26 мкс (GCSuspendEEEndGCSuspendEEBegin_V1).

    Фактическая сборка мусора заняла 4,8 мс (GCEnd_V1GCStart_V1).

    Возобновление управляемых потоков заняло 21us (GCRestartEEEndGCRestartEEBegin).

    Приведенные ниже выходные данные содержат пример для процесса фоновой сборки мусора и включают поля, относящиеся к процессу, потоку и событию. (Не все данные показаны.)

    timestamp(us)    event name            process    thread    event field
    42504385        GCSuspendEEBegin_V1    Test.exe    4372             1
    42504648        GCSuspendEEEnd         Test.exe    4372
    42504816        GCStart_V1             Test.exe    4372        102019
    42504907        GCStart_V1             Test.exe    4372        102020
    42514170        GCEnd_V1               Test.exe    4372
    42514204        GCHeapStats            Test.exe    4372        102020
    42832052        GCRestartEEBegin       Test.exe    4372
    42832136        GCRestartEEEnd         Test.exe    4372
    63685394        GCSuspendEEBegin_V1    Test.exe    4744             6
    63686347        GCSuspendEEEnd         Test.exe    4744
    63784294        GCRestartEEBegin       Test.exe    4744
    63784407        GCRestartEEEnd         Test.exe    4744
    89931423        GCEnd_V1               Test.exe    4372        102019
    89931464        GCHeapStats            Test.exe    4372
    

    Событие GCStart_V1 на 42504816 указывает, что это фоновая очистка мусора, так как последнее поле — это 1. Это становится коллекцией мусора № 102019.

    Это GCStart событие возникает из-за необходимости в эфемерной сборке мусора перед началом фоновой сборки мусора. Это становится сбором мусора № 102020.

    По адресу 42514170 сборка мусора № 102020 завершена. Управляемые потоки перезапускаются на этом этапе. Это выполнено в потоке 4372, который инициировал эту фоновую сборку мусора.

    В потоке 4744 происходит приостановка. Это единственный раз, когда фоновая сборка мусора должна приостановить управляемые потоки. Эта длительность составляет примерно 99 мс ((6378407-63685394)/1000.

    Событие GCEnd для фоновой сборки мусора зарегистрировано на уровне 89931423. Это означает, что фоновая сборка мусора длилась около 47 секунд ((89931423-42504816)/1000).

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

Определение того, что активировало сборку мусора

  • В отладчике WinDbg или Visual Studio с загруженным расширением отладчика SOS введите следующую команду, чтобы отобразить все потоки со стеками вызовов:

    ~*бк

    Эта команда отображает выходные данные, аналогичные приведенному ниже.

    0012f3b0 79ff0bf8 mscorwks!WKS::GCHeap::GarbageCollect
    0012f454 30002894 mscorwks!GCInterface::CollectGeneration+0xa4
    0012f490 79fa22bd fragment_ni!request.Main(System.String[])+0x48
    

    Если сборка мусора вызвана уведомлением операционной системы о нехватке памяти, стек вызовов аналогичен, но поток является потоком финализатора. Поток завершения получает асинхронное уведомление о низком уровне памяти и инициирует сборку мусора.

    Если сборка мусора была вызвана выделением памяти, стек отображается следующим образом:

    0012f230 7a07c551 mscorwks!WKS::GCHeap::GarbageCollectGeneration
    0012f2b8 7a07cba8 mscorwks!WKS::gc_heap::try_allocate_more_space+0x1a1
    0012f2d4 7a07cefb mscorwks!WKS::gc_heap::allocate_more_space+0x18
    0012f2f4 7a02a51b mscorwks!WKS::GCHeap::Alloc+0x4b
    0012f310 7a02ae4c mscorwks!Alloc+0x60
    0012f364 7a030e46 mscorwks!FastAllocatePrimitiveArray+0xbd
    0012f424 300027f4 mscorwks!JIT_NewArr1+0x148
    000af70f 3000299f fragment_ni!request..ctor(Int32, Single)+0x20c
    0000002a 79fa22bd fragment_ni!request.Main(System.String[])+0x153
    

    Помощник, работающий по принципу "точно в срок" (JIT_New*), в конечном итоге вызывает GCHeap::GarbageCollectGeneration. Если определить, что сборки мусора поколения 2 вызваны выделениями, необходимо определить, какие объекты собираются сборкой мусора поколения 2 и как избежать их. То есть необходимо определить разницу между началом и окончанием сборки мусора поколения 2 и объектами, вызвавших коллекцию поколения 2.

    Например, введите следующую команду в отладчике, чтобы отобразить начало коллекции поколения 2:

    !dumpheap –stat

    Пример выходных данных (сокращенный для отображения объектов, использующих наибольшее пространство):

    79124228    31857      9862328 System.Object[]
    035f0384    25668     11601936 Toolkit.TlkPosition
    00155f80    21248     12256296      Free
    79103b6c   297003     13068132 System.Threading.ReaderWriterLock
    7a747ad4   708732     14174640 System.Collections.Specialized.HybridDictionary
    7a747c78   786498     15729960 System.Collections.Specialized.ListDictionary+DictionaryNode
    7a747bac   700298     19608344 System.Collections.Specialized.ListDictionary
    035f0ee4    89192     38887712 Toolkit.TlkOrder
    00fcae40     6193     44911636 WaveBasedStrategy.Tick_Snap[]
    7912c444    91616     71887080 System.Double[]
    791242ec    32451     82462728 System.Collections.Hashtable+bucket[]
    790fa3e0  2459154    112128436 System.String
    Total 6471774 objects
    

    Повторите команду в конце поколения 2:

    !dumpheap –stat

    Пример выходных данных (сокращенный для отображения объектов, использующих наибольшее пространство):

    79124228    26648      9314256 System.Object[]
    035f0384    25668     11601936 Toolkit.TlkPosition
    79103b6c   296770     13057880 System.Threading.ReaderWriterLock
    7a747ad4   708730     14174600 System.Collections.Specialized.HybridDictionary
    7a747c78   786497     15729940 System.Collections.Specialized.ListDictionary+DictionaryNode
    7a747bac   700298     19608344 System.Collections.Specialized.ListDictionary
    00155f80    13806     34007212      Free
    035f0ee4    89187     38885532 Toolkit.TlkOrder
    00fcae40     6193     44911636 WaveBasedStrategy.Tick_Snap[]
    791242ec    32370     82359768 System.Collections.Hashtable+bucket[]
    790fa3e0  2440020    111341808 System.String
    Total 6417525 objects
    

    Исчезновение объектов double[] из результирующего вывода означает, что они собраны. Эти объекты составляют около 70 МБ. Остальные объекты не изменились. Поэтому эти double[] объекты были причиной, по которой произошла сборка мусора поколения 2. Следующий шаг — определить, почему double[] объекты там и почему они умерли. Вы можете попросить разработчика кода, откуда пришли эти объекты, или использовать gcroot команду.

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

  • Сопоставляйте % Time in GC значение счетчика производительности памяти со временем процесса.

    % Time in GC Если значение резко возрастает одновременно с временем процесса, сборка мусора вызывает высокую загрузку ЦП. В противном случае профилируйте приложение, чтобы найти место, где возникает высокая нагрузка.

См. также