Оптимизация производительности: поведение объекта

Обновлен: Ноябрь 2007

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

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

  • Сохранение обработчиков событий для объектов может поддерживать объекты в активном состоянии
  • Объекты и свойства зависимостей
  • Объекты Freezable
  • Визуализация пользовательского интерфейса
  • Связанные разделы

Сохранение обработчиков событий для объектов может поддерживать объекты в активном состоянии

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

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

Существует несколько средств, таких как CLR Profiler и Working Set Viewer, которые могут предоставить информацию об использовании памяти указанным процессом. CLR Profiler включает ряд очень полезных представлений профиля распределения, включая гистограмму выделенных типов, диаграмму выделения памяти и вызовов, шкала времени, показывающая сборку мусора различных поколений, результирующее состояние управляемой кучи после этих сборок и дерево вызовов, показывающее нагрузку на сборку и распределение для каждого метода. Дополнительные сведения см. в Центре разработчиков Microsoft .NET Framework.

Working Set Viewer является средством анализа производительности WPF, предоставляющим сведения об использовании памяти указанным процессом. Это средство позволяет создавать моментальный снимок использования памяти приложением в его определенном состоянии. Дополнительные сведения о средствах производительности WPF см. в разделе Средства профилирования производительности для WPF.

Объекты и свойства зависимостей

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

Оптимизация свойств зависимостей

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

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

Создание наследуемого DependencyProperty

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

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

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

Установка значения по умолчанию для DependencyProperty во время регистрации

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

Задание значения PropertyMetadata с помощью элемента Register

При создании DependencyProperty имеется возможность задания PropertyMetadata с помощью метода Register или OverrideMetadata. Хотя объект может иметь статический конструктор для вызова OverrideMetadata, его использование не является оптимальным решением и повлияет на производительность. Для достижения наилучшей производительности задайте PropertyMetadata во время вызова Register.

Объекты Freezable

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

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

Рассмотрим следующий пример, в котором каждый Rectangle использует один и тот же объект Brush:

rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;

По умолчанию WPF предоставляет обработчик событий для события Changed объекта SolidColorBrush, делающий недействительным свойство Fill объекта Rectangle. В этом случае каждый раз, когда SolidColorBrush вынуждена вызывать событие Changed, необходимо вызывать функцию обратного вызова для каждого Rectangle — накопление вызовов этих функций значительно снижает производительность. Кроме того, достаточно затратно добавлять и удалять обработчики на этом этапе, поскольку для этого приложению потребуется пройти по всему списку. Если сценарий приложения никогда не изменяет SolidColorBrush, то последует "расплата" за обслуживание вызываемых без необходимости обработчиков событий Changed.

Замораживание элементов Freezable может повысить быстродействие, поскольку больше не требуется тратить ресурсы на уведомления об изменениях. В нижеприведенной таблице показано сравнение размера простого SolidColorBrush, когда его свойство IsFrozen установлено в true, и когда нет. Это предполагает применение одной кисти к свойству Fill десяти объектов Rectangle.

Состояние

Размер

Замороженная SolidColorBrush

212 байт

Незамороженная SolidColorBrush

972 байта

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

Brush frozenBrush = new SolidColorBrush(Colors.Blue);
frozenBrush.Freeze();
Brush nonFrozenBrush = new SolidColorBrush(Colors.Blue);

for (int i = 0; i < 10; i++)
{
    // Create a Rectangle using a non-frozed Brush.
    Rectangle rectangleNonFrozen = new Rectangle();
    rectangleNonFrozen.Fill = nonFrozenBrush;

    // Create a Rectangle using a frozed Brush.
    Rectangle rectangleFrozen = new Rectangle();
    rectangleFrozen.Fill = frozenBrush;
}

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

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

WPF также внутренне подключается к событиям Changed. Например, все свойства зависимостей, которые принимают Freezable в качестве значения, будут автоматически прослушивать события Changed. Свойство Fill, которое принимает Brush, иллюстрирует эту концепцию.

Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;

При присваивании элемента myBrush для myRectangle.Fill делегат, указывающий обратно на объект Rectangle, будет добавлен к событию Changed объекта SolidColorBrush. Это означает, что следующий код фактически не помечает myRect для сборки мусора:

myRectangle = null;

В этом случае myBrush по-прежнему сохраняет myRectangle в активном состоянии и снова вызовет его при возникновении события Changed. Обратите внимание, что присваивание myBrush свойству Fill нового Rectangle просто добавит еще один обработчик событий к myBrush.

Рекомендуемый способ очистки этих типов объектов — удаление Brush из свойства Fill, которое в свою очередь удалит обработчик событий Changed.

myRectangle.Fill = null;
myRectangle = null;

Визуализация пользовательского интерфейса

WPF также предоставляет разновидность элемента StackPanel, который автоматически "делает виртуальным" содержимое дочернего элемента, привязанного к данным. В данном контексте слово "виртуализация" означает способ, с помощью которого подмножество UIElements создается из большого количества элементов данных на основе видимых на экране элементов. Как для памяти, так и для процессора затратно создавать большое число элементов пользовательского интерфейса, при том что только несколько из них могут отображаться на экране одновременно. VirtualizingStackPanel (с помощью функций, предоставляемых VirtualizingPanel) вычисляет видимые элементы и работает с ItemContainerGenerator из ItemsControl (например, ListBox или ListView) только для создания UIElements для видимых элементов.

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

В таблице ниже показано время, затраченное на добавление и отрисовку 5 000 элементов TextBlock к StackPanel и VirtualizingStackPanel. В этом случае измеряется время между временем присоединения текстовой строки к свойству ItemsSource объекта ItemsControl и временем отображения текстовой строки элементами панели.

Главная панель

Время отображения (мс)

StackPanel

3210

VirtualizingStackPanel

46

См. также

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

Улучшение производительности приложений WPF

Планирование производительности приложения

Оптимизация производительности. Использование преимуществ аппаратного ускорения

Оптимизация производительности: разметка и разработка

Оптимизация производительности: двумерная графика и обработка изображений

Оптимизация производительности: ресурсы приложения

Оптимизация производительности: отображение текста

Оптимизация производительности: привязка данных

Оптимизация производительности. Дополнительные рекомендации

Средства и ресурсы обеспечения производительности WPF