Улучшение производительности сборки мусора
приложения универсальная платформа Windows (UWP), написанные на C# и Visual Basic, получают автоматическое управление памятью от сборщика мусора .NET. В этом разделе приведены рекомендации по поведению и производительности сборщика мусора .NET в приложениях UWP. Дополнительные сведения о работе сборщика мусора .NET и средствах для отладки и анализа производительности сборщика мусора см. в статье "Сборка мусора".
Примечание. Необходимость изменить поведение сборщика мусора, заданное по умолчанию, свидетельствует о наличии распространенных проблем с памятью в вашем приложении. Дополнительные сведения см. в разделе "Средство использования памяти" при отладке в Visual Studio 2015. Этот раздел относится только к C# и Visual Basic.
Сборщик мусора определяет, когда следует выполнять балансировку потребления памяти управляемой кучи с объемом работы сборки мусора. Одним из способов того, как сборщик мусора делает это, разделив кучу на поколения и собирая только часть кучи большую часть времени. Существует три поколения в управляемой куче:
- Поколение 0. Это поколение содержит только что выделенные объекты, если они не имеют 85 КБ или больше, в этом случае они являются частью большой кучи объектов. Большая куча объектов собирается с коллекциями поколения 2. Коллекции поколения 0 — это наиболее часто встречающийся тип коллекции и очистка краткосрочных объектов, таких как локальные переменные.
- Поколение 1. Это поколение содержит объекты, которые пережили коллекции поколения 0. Он служит буфером между поколением 0 и поколением 2. Коллекции поколения 1 происходят реже, чем коллекции поколения 0 и очищают временные объекты, активные в предыдущих коллекциях поколения 0. Коллекция поколения 1 также собирает поколение 0.
- Поколение 2. Это поколение содержит долгоживущие объекты, которые пережили коллекции поколения 0 и поколения 1. Коллекции поколения 2 являются наименее частыми и собирают всю управляемую кучу, в том числе большую кучу объектов, содержащую объекты размером 85 КБ или больше.
Производительность сборщика мусора можно измерять в 2 аспектах: время, необходимое для сборки мусора, а также потребление памяти управляемой кучи. Если у вас есть небольшое приложение с размером кучи менее 100 МБ, то сосредоточьтесь на снижении потребления памяти. Если у вас есть приложение с управляемой кучей размером более 100 МБ, то сосредоточьтесь только на сокращении времени сборки мусора. Вот как можно помочь сборщику мусора .NET повысить производительность.
Уменьшение потребления памяти
Ссылки на выпуск
Ссылка на объект в приложении предотвращает сбор этого объекта и всех объектов, на которые он ссылается. Компилятор .NET хорошо определяет, когда переменная больше не используется, поэтому объекты, удерживаемые этой переменной, будут иметь право на коллекцию. Но в некоторых случаях может быть не очевидно, что некоторые объекты имеют ссылку на другие объекты, так как часть графа объектов может принадлежать библиотекам, которые использует ваше приложение. Дополнительные сведения о средствах и методах для получения сведений о том, какие объекты выживают в сборке мусора, см. в статье "Сборка мусора" и производительность.
Вызвать сборку мусора, если это полезно
Вызов сборки мусора только после того, как вы измеряли производительность приложения и определили, что принудить коллекцию повысить его производительность.
Вы можете вызвать сборку мусора поколения, вызвав GC. Collect(n), где n — это поколение, которое требуется собрать (0, 1 или 2).
Примечание. Мы не рекомендуем инициировать сборку мусора в приложении, поскольку сборщик использует множество эвристических алгоритмов для выбора оптимального времени своего запуска и принудительная сборка во многих случаях приводит к ненужному расходованию ресурсов процессора. Но если вы знаете, что у вас есть большое количество объектов в приложении, которые больше не используются, и вы хотите вернуть эту память в систему, то может потребоваться принудительной сборки мусора. Например, вы можете вызвать коллекцию в конце последовательности загрузки в игре, чтобы освободить память до начала игрового процесса. Чтобы избежать непреднамеренной индукции слишком большого количества сборок мусора, можно задать для GCCollectionMode значение "Оптимизировано". Это указывает сборщику мусора запустить коллекцию только в том случае, если она определяет, что коллекция будет достаточно продуктивной, чтобы быть оправданной.
Сокращение времени сборки мусора
Этот раздел применяется, если вы проанализировали приложение и наблюдали время сборки мусора. Время приостановки сборки мусора: время, необходимое для выполнения одного прохода сборки мусора; и общее время, которое приложение тратит на сборки мусора. Время, необходимое для сбора, зависит от того, сколько динамических данных сборщик должен анализировать. Поколение 0 и поколение 1 привязаны к размеру, но поколение 2 продолжает расти, так как более длительные объекты активны в приложении. Это означает, что время сбора для поколения 0 и поколения 1 привязано, а коллекции поколения 2 могут занять больше времени. Насколько часто сборки мусора выполняются, в основном зависит от объема выделенной памяти, так как сборка мусора освобождает память для удовлетворения запросов на выделение.
Сборщик мусора иногда приостанавливает работу приложения, но не обязательно приостанавливает все время выполнения коллекции. Время приостановки обычно не является понятным для пользователя в приложении, особенно для коллекций поколения 0 и поколения 1. Функция фоновой сборки мусора сборщика мусора .NET позволяет одновременно выполнять коллекции поколения 2 во время работы приложения и приостанавливать приложение только в течение короткого периода времени. Но не всегда можно сделать коллекцию поколения 2 как фоновую коллекцию. В этом случае пауза может быть определяемой пользователем, если у вас достаточно большой кучи (более 100 МБ).
Частые сборки мусора могут способствовать увеличению потребления ЦП (и, следовательно, энергопотребления), более длительному времени загрузки или уменьшению частоты кадров в приложении. Ниже приведены некоторые методы, которые можно использовать для уменьшения времени сборки мусора и приостановки сбора в управляемом приложении UWP.
Сокращение выделения памяти
Если вы не выделяете какие-либо объекты, то сборщик мусора не выполняется, если в системе нет условия низкой памяти. Уменьшение объема памяти, которую вы выделяете напрямую, преобразуется в менее частые сборки мусора.
Если в некоторых разделах приложения паузы полностью нежелательны, вы можете заранее выделить необходимые объекты в течение менее критического времени производительности. Например, игра может выделить все объекты, необходимые для игрового процесса во время загрузки экрана уровня, и не делать выделения во время игрового процесса. Это позволяет избежать пауз, пока пользователь играет в игру, и может привести к более высокой и более согласованной частоте кадров.
Сокращение коллекций поколения 2 путем предотвращения объектов со временем существования среднего времени существования
Сборки мусора поколения лучше всего выполняются, если в приложении имеются очень короткие и (или) действительно длительные объекты. Кратковременные объекты собираются в более дешевых коллекциях поколения 0 и поколения 1, а объекты, которые являются долгоживующими, повышаются до поколения 2, которые собираются редко. Долгоживущие объекты — это объекты, которые используются в течение всего периода работы приложения или в течение значительного периода приложения, например на определенном уровне страницы или игры.
Если вы часто создаете объекты с временным временем существования, но достаточно долго живут, чтобы быть повышен до поколения 2, то происходит больше дорогих коллекций поколения 2. Вы можете уменьшить коллекцию поколения 2 путем повторной переработки существующих объектов или более быстрого освобождения объектов.
Распространенный пример объектов со средним временем существования — это объекты, используемые для отображения элементов в списке, который прокручивает пользователь. Если объекты создаются, когда элементы в списке прокручиваются в представление и больше не ссылаются, так как элементы в списке прокручиваются вне представления, приложение обычно имеет большое количество коллекций поколения 2. В таких ситуациях можно предварительно выделить и повторно использовать набор объектов для данных, которые активно отображаются пользователю, и использовать короткие объекты для загрузки информации в виде элементов в списке.
Сокращение коллекций поколения 2 путем предотвращения больших размеров объектов с коротким временем существования
Любой объект размером 85 КБ или больше выделяется в куче больших объектов (LOH) и собирается в составе поколения 2. Если у вас есть временные переменные, такие как буферы, которые больше 85 КБ, то коллекция поколения 2 очищает их. Ограничение временных переменных менее 85 КБ уменьшает количество коллекций поколения 2 в приложении. Одним из распространенных способов является создание буферного пула и повторное использование объектов из пула, чтобы избежать больших временных выделений.
Избегайте объектов с расширенными ссылками
Сборщик мусора определяет, какие объекты живут, следуя ссылкам между объектами, начиная с корней в приложении. Дополнительные сведения см. в разделе "Что происходит во время сборки мусора". Если объект содержит много ссылок, то для сборщика мусора требуется больше работы. Распространенный прием (особенно для больших объектов) — преобразовать объекты со множеством ссылок в объекты без ссылок (например, вместо ссылки хранить индекс). Конечно, этот метод работает только тогда, когда это логически возможно.
Замена ссылок на объекты индексами может быть разрушительным и сложным изменением приложения и наиболее эффективным для больших объектов с большим количеством ссылок. Это делается только в том случае, если вы замечаете большое время сборки мусора в приложении, связанном с эталонными объектами.