Сборка мусора и производительность
В этой статье описываются проблемы, связанные с сбором мусора и использованием памяти. Здесь рассматриваются проблемы, относящиеся к управляемой куче, и объясняется, как свести к минимуму влияние сборки мусора на работу приложений. Для каждого аспекта приводятся ссылки на процедуры, которые можно использовать для анализа проблем.
Средства анализа производительности
В следующих разделах описываются средства, которые можно использовать для диагностики проблем с использованием памяти и сборкой мусора. Описанные далее в этой статье процедуры относятся к этим средствам.
Счетчики производительности памяти
Счетчики производительности можно использовать для сбора данных производительности. Инструкции см. в разделе Профилирование среды выполнения. Категория счетчиков производительности "Память CLR .NET" (как описано в разделе Счетчики производительности в .NET) предоставляет сведения о сборщике мусора.
Отладка с помощью расширения SOS
Для проверки объектов в управляемой куче можно использовать отладчик Windows (WinDbg).
Чтобы установить WinDbg, установите средства отладки для Windows со страницы скачивания средств отладки для Windows.
События сборки мусора (трассировка событий Windows)
Трассировка событий Windows (ETW) — это система трассировки, дополняющая поддержку профилирования и отладки в .NET. Начиная с .NET Framework 4, события ETW для сборки мусора собирают полезные сведения для анализа управляемой кучи с точки зрения статистики. Например, событие GCStart_V1
, которое вызывается перед началом сборки мусора, предоставляет следующие сведения:
- собираемое поколение объектов;
- что активировало сборку мусора;
- тип сборки мусора (параллельная или непараллельная).
Ведение журнала событий трассировки событий Windows эффективно и не скрывает проблемы производительности, связанные со сборкой мусора. Процесс может предоставлять свои собственные события вместе с событиями трассировки событий Windows. При регистрации в журнале события приложения можно соотнести с событиями сборки мусора, чтобы определить, как и когда возникают проблемы кучи. Например, серверное приложение может предоставлять события в начале и в конце запроса клиента.
API профилирования
Интерфейсы профилирования среды выполнения (CLR) предоставляют подробные сведения об объектах, которые были затронуты во время сборки мусора. Профилировщик может получать уведомления о начале и завершении сборки мусора. Он может предоставлять отчеты об объектах в управляемой куче, включая идентификацию объектов в каждом поколении. Дополнительные сведения см. в разделе Общие сведения о профилировании.
Профилировщики могут предоставлять исчерпывающую информацию. Однако сложные профилировщики потенциально могут менять поведение приложения.
Отслеживание ресурсов домена приложения
Начиная с .NET Framework 4, функция отслеживания ресурсов домена приложения (ARM) позволяет узлам отслеживать загрузку ЦП и использование памяти доменом приложения. Дополнительные сведения см. в разделе Отслеживание ресурсов домена приложения.
Устранение проблем с производительностью
Первый этап — определить, действительно ли проблема относится к сборке мусора. Если это так, выберите одну из проблем в списке ниже, чтобы определить способ ее устранения.
- Создается исключение нехватки памяти
- Процесс использует слишком много памяти
- Сборщик мусора недостаточно быстро освобождает объекты
- Управляемая куча слишком сильно фрагментирована
- Слишком большие интервалы между сборками мусора
- Слишком большой размер поколения 0
- Слишком высокая загрузка ЦП во время сборки мусора
Проблема: создается исключение нехватки памяти
Существует два допустимых случая создания управляемого исключения OutOfMemoryException:
нехватка виртуальной памяти;
Сборщик мусора распределяет память из системы в виде сегментов заранее заданного размера. Если выделению требуется дополнительный сегмент, но в пространстве виртуальной памяти процесса не осталось непрерывных свободных блоков, произойдет сбой выделения памяти для управляемой кучи.
нехватка физической памяти для выделения.
Если выяснилось, что исключение не относится к допустимым сценариям, обратитесь в службу технической поддержки Майкрософт, сообщив следующие сведения:
- стек с управляемым исключением нехватки памяти,
- полный дамп памяти,
- данные, подтверждающие недопустимый характер исключения нехватки памяти, включая данные, показывающие, что проблема не связана с виртуальной или физической памятью.
Проблема: процесс использует слишком много памяти
Считается, что с помощью индикатора использования памяти на вкладке Быстродействие диспетчера задач Windows можно определить, что процесс использует слишком много памяти. Однако этот индикатор относится к рабочему набору; он не предоставляет сведения об использовании виртуальной памяти.
Если выяснилось, что проблема вызвана управляемой кучей, необходимо провести для управляемой кучи измерения в динамике, чтобы определить наличие каких-либо шаблонов.
Если выяснилось, что проблема не вызвана управляемой кучей, необходимо использовать отладку неуправляемого кода.
Проблема: сборщик мусора недостаточно быстро освобождает объекты
Если есть признаки того, что объекты не освобождаются сборкой мусора должным образом, необходимо определить, нет ли строгих ссылок на эти объекты.
Эта проблема также может возникать, если не выполнялась сборка мусора для поколения, содержащего неиспользуемый объект. Это является признаком того, что для неиспользуемого объекта не выполнялся метод завершения. Например, это может происходить, когда вы запускаете приложение однопотокового подразделения (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.
Процедуры проверка производительности
В этом разделе описаны следующие процедуры по выявлению причин проблем с производительностью.
- Определите, вызвана ли проблема сборкой мусора.
- Определите, является ли исключение нехватки памяти управляемым.
- Определите, сколько виртуальной памяти можно зарезервировать.
- Определите, имеется ли достаточный объем физической памяти.
- Определите объем памяти, зафиксированный управляемой кучей.
- Определите объем памяти, зарезервированный управляемой кучей.
- Определите большие объекты в поколении 2.
- Определите ссылки на объекты.
- Определите, выполнялся ли метод завершения.
- Определите наличие объектов, ожидающих завершения.
- Определите объем свободного пространства в управляемой куче.
- Определите количество закрепленных объектов.
- Определите продолжительность сборки мусора.
- Определите, что вызвало сборку мусора.
- Определите, вызвана ли высокая загрузка ЦП сборкой мусора.
Чтобы определить, вызвана ли проблема сборкой мусора, выполните следующие действия.
Проверьте следующие два счетчика производительности памяти:
% времени в сборке мусора. Отображение времени, потраченного на выполнение сборки мусора с момента последнего цикла сборки мусора (в процентах). Этот счетчик используется, чтобы определить, тратит ли сборщик мусора слишком много времени на освобождение пространства в управляемой куче. Если время, затраченное на сборку мусора, сравнительно мало, это может указывать на проблему ресурсов за пределами управляемой кучи. Этот счетчик может предоставлять неточные данные при участии параллельной или фоновой сборки мусора.
Всего зафиксировано байт. Отображение объема виртуальной памяти, в данный момент выделенной сборщиком мусора. С помощью этого счетчика можно определить, является ли память, используемая сборщиком мусора, избыточной частью памяти, которую использует приложение.
Большинство счетчиков памяти обновляются в конце каждой сборки мусора. Таким образом, они могут не отражать текущие условия, сведения о которых вам требуются.
Чтобы определить, является ли исключение нехватки памяти управляемым, выполните следующие действия.
В отладчике WinDbg или Visual Studio с расширением отладчика SOS, загруженном, введите команду исключения печати (
pe
) :!pe
Если исключение является управляемым, OutOfMemoryException отображается как тип исключения, как показано в следующем примере.
Exception object: 39594518 Exception type: System.OutOfMemoryException Message: <none> InnerException: <none> StackTrace (generated):
Если выходные данные не указывают на исключение, необходимо определить, к какому потоку относится исключение нехватки памяти. Введите следующую команду в отладчике, чтобы отобразить все потоки со своими стеками вызовов:
~\*kb
Поток со стеком, для которого есть вызовы исключений, обозначается аргументом
RaiseTheException
. Это объект управляемого исключения.28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
Чтобы создать дамп вложенных исключений, можно использовать следующую команду.
!pe -nested
Если исключения не обнаружены, значит исключение нехватки памяти возникло в неуправляемом коде.
Чтобы определить, сколько виртуальной памяти можно зарезервировать, выполните следующие действия.
В WinDbg с загруженным расширением отладчика SOS введите следующую команду, чтобы получить самый большой бесплатный регион:
!address -summary
Самая крупная свободная область отображается, как показано в выходных данных команды ниже.
Largest free region: Base 54000000 - Size 0003A980
В этом примере размер самой крупной свободной области составляет приблизительно 24 000 КБ (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
Чтобы определить, имеется ли достаточный объем физической памяти, выполните следующие действия.
Запустите диспетчер задач Windows.
На вкладке просмотрите
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)
Адреса с отметкой 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
–или–
Чтобы определить ссылки для конкретного объекта, включите соответствующий адрес:
!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 позволяет методам завершения выполнить свои задачи и устраняет проблему.
Чтобы определить наличие объектов, ожидающих завершения, выполните следующие действия.
В отладчике WinDbg или Visual Studio с загруженным расширением отладчика SOS введите следующую команду:
!finalizequeue
Проверьте число объектов, которые готовы к завершению. Если число большое, необходимо выяснить, почему эти методы завершения вообще не могут выполняться или не могут выполняться достаточно быстро.
Чтобы получить выходные данные потоков, введите следующую команду:
!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-секундного исследования.
Gen1
СтолбцыGen0
и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
Второе поколение 2 сборка мусора началась во время четвертого интервала и завершилась на пятом интервале. Предполагая худший случай, последняя сборка мусора была для коллекции поколения 0, которая закончилась в начале третьего интервала, и сборка мусора поколения 2 закончилась в конце пятого интервала. Таким образом, время между завершением сборки мусора для поколения 0 и завершением сборки мусора для поколения 2 составляет 4 секунды. Поскольку счетчик
% Time in GC
выдает значение 20 %, максимальная длительность сборки мусора поколения 2 могла составлять (4 секунды * 20 % =) 800 мс.Кроме того, продолжительность сборки мусора можно определить с помощью событий ETW для сборки мусора, проанализировав данные для выяснения длительности сборки мусора.
Например, следующие данные показывают последовательность событий, возникших во время непараллельной сборки мусора.
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
. Это становится сборкой мусора No. 102019.Событие
GCStart
возникает из-за необходимости выполнить эфемерную сборку мусора до запуска фоновой сборки мусора. Это становится сборкой мусора No 102020.На 42514170 сборка мусора No 102020 завершается. На этом этапе перезапускаются управляемые потоки. Это действие выполняется для потока 4372, активировавшего эту фоновую сборку мусора.
Для потока 4744 происходит приостановка. Это единственный случай, когда фоновой сборке мусора приходится приостанавливать управляемые потоки. Продолжительность приостановки составляет примерно 99 мс ((63784407-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-объект (
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
резко возрастает одновременно с временем обработки, значит сборка мусора вызывает высокую загрузку ЦП. В противном случае выполните профилирование приложения, чтобы определить, где возникает высокая загрузка.