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


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

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

В этом разделе содержатся следующие подразделы.

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

  • Вопросы устранения проблем производительности

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

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

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

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

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

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

Отладчики с SOS

Для проверки объектов в управляемой куче можно использовать отладчик Windows (WinDbg) или отладчик Visual Studio с SOS.dll (расширение отладки SOS).

Для установки WinDbg установите пакет средств отладки для Windows с веб-сайта WDK and Developer Tools. Сведения об использовании расширения отладки SOS см. в разделе Практическое руководство. Использование SOS.

События сборки мусора (трассировка событий Windows)

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

  • какое поколение объектов подвергается сборке;

  • причина запуска сборки мусора;

  • тип сборки мусора (параллельная или непараллельная).

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

Интерфейс API для профилирования

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

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

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

Начиная с .NET Framework 4, отслеживание ресурсов домена приложения (ARM) позволяет основным приложениям следить за тем, как домен приложения использует ресурсы процессора и памяти. Дополнительные сведения см. в разделе Отслеживание ресурсов домена приложения.

К началу

Вопросы устранения проблем производительности

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

  • Возникло исключение нехватки памяти

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

  • Сборщик мусора недостаточно быстро удаляет объекты

  • Управляемая куча слишком сильно фрагментирована

  • Интервалы между сборками мусора слишком продолжительны

  • Поколение 0 слишком велико

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

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

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

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

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

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

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

Определите, управляется ли исключение нехватки памяти.

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

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

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

  • стек с управляемым исключением нехватки памяти;

  • полный дамп памяти;

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

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

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

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

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

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

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

Определите, сколько памяти выделяет управляемая куча.

Определите, сколько памяти резервирует управляемая куча.

Определите крупные объекты в поколении 2.

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

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

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

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

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

Проверьте ссылки на объекты.

Определите, выполнен ли метод завершения.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Для определения того, приближается ли сборка поколения 2 на сервере, и того, могут ли запросы перенаправления на другой сервер частично решить какие-либо проблемы с интервалами, можно использовать Уведомления о сборке мусора.

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

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

Определите, что вызвало сборку мусора.

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

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

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

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

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

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

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

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

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

К началу

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

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

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

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

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

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

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

  • Если измерения проводятся после сборки мусора поколения 2, вся управляемая куча будет очищена от мусора (неиспользуемых объектов).

  • Если измерения проводятся сразу после сборки мусора поколения 0, объекты в поколениях 1 и 2 еще не будут собраны.

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

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

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

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

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

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

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

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

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

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

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

К началу

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

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

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

  • Определите, управляется ли исключение нехватки памяти.

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

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

  • Определите, сколько памяти выделяет управляемая куча.

  • Определите, сколько памяти резервирует управляемая куча.

  • Определите крупные объекты в поколении 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. Посмотрите значение выделения на вкладке Быстродействие. (В Windows 7 см. Выделено (КБ) в группе Система).

    Если значение Всего близко к значению Предел, объем физической памяти низкий.

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

  • Используйте счетчик производительности памяти # 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)
    

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

Определение крупных объектов в поколении 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

    -or-

  • Для определения ссылок на конкретный объект включите соответствующий адрес:

    !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 мс).

    В данном примере приведены 5 периодов.

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

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

  • Также продолжительность сборки мусора можно определить путем использования событий сборки мусора трассировки событий Windows и анализа этой информации для определения продолжительности сборки мусора.

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

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

    На приостановку управляемого потока затрачено 26 мкс (GCSuspendEEEnd – GCSuspendEEBegin_V1).

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

    На возобновление управляемых потоков ушло 21 мкс (GCRestartEEEnd – GCRestartEEBegin).

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

    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 мс ((63784407-63685394)/1000).

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

    Во время выполнения управляемых потоков можно просматривать любое количество выполняемых эфемерных сборок мусора.

Определение причины запуска сборки мусора

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

    ~*kb

    Эта команда отображает подобный следующему вывод.

    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 (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 резко возрастает одновременно с процессорным временем, сборка мусора вызывает высокую загрузку ЦП. В противном случае необходимо выполнить профилирование приложения для определения того, где происходит высокая загрузка.

См. также

Основные понятия

Сборка мусора