Otimizando o desempenho: Comportamento de objeto
Entender o comportamento intrínseco de WPF ajudará a fazer o balanceamento correto entre funcionalidade e desempenho.
Este tópico contém as seguintes seções.
- Não remover manipuladores de eventos em objetos pode manter objetos ativos
- Propriedades e objetos de dependência
- Objetos Congeláveis
- Virtualização de interface do usuário
- Tópicos relacionados
Não remover manipuladores de eventos em objetos pode manter objetos ativos
O representante que um objeto passa para seu evento é efetivamente uma referência ao objeto. Portanto, os manipuladores de eventos podem manter objetos ativos mais do que o esperado. Ao executar limpeza de um objeto que tenha sido registrado para escutar um evento de objeto, é essencial remover esse representante antes de liberar o objeto. Manter ativos objetos desnecessários aumenta o uso de memória da aplicação. Isso é especialmente verdadeiro quando o objeto é a raiz de uma árvore lógica ou de uma árvore visual .
WPF apresenta um padrão de escuta fraco evento para eventos que pode ser útil em situações onde o controle de relações de vida útil de objeto entre origem e de escuta são difíceis de serem mantidas. Alguns eventos WPF existentes usam esse padrão. Se você estiver implementando objetos com eventos personalizados, esse padrão pode ser útil para você. Para obter detalhes, consulte:Padrões WeakEvent.
Há várias ferramentas, como o Profiler CLR e o Working Set Viewer, que pode fornecer informações sobre o uso de memória de um processo especificado. O Profiler CLR inclui um número de exibições muito úteis do perfil de alocação, inclusive um histograma de tipos alocados, grafos de alocação e chamada, uma linha de tempo mostrando coleções de lixo de várias gerações e o estado resultante de heap gerenciado após essas coleções, e uma árvore de chamada mostrando, por método, alocações e carregamento de conjunto de módulos (assembly). Para obter mais informações, consulte .NET estrutura Developer centralizar.
O Working Set Viewer é uma ferramenta de análise de desempenho WPF que fornece informações sobre o uso de memória de um processo especificado. Essa ferramenta permite que você gere um snapshot de informações de uso de memória do aplicativo em um determinado estado do aplicativo. Para obter mais informações sobre ferramentas de desempenho WPF, consulte Desempenho perfil ferramentas para WPF.
Propriedades e objetos de dependência
Em geral, acessar uma propriedade de dependência de um DependencyObject não é mais lento que acessar uma propriedade CLR. Enquanto houver uma pequena sobrecarga de desempenho para definir um valor da propriedade, obter um valor é tão rápido quanto obter o valor de uma propriedade CLR. Compensar a pequena sobrecarga de desempenho é o fato que propriedades de dependência suportam recursos eficientes, como associação de dados, animação, herança e estilos. Para obter mais informações, consulte Visão geral sobre propriedades de dependência.
Otimizações de DependencyProperty
Você deve definir propriedades de dependência em seu aplicativo com muito cuidado. Se sua DependencyProperty afeta somente opções de metadados de tipos de rendering, em vez de outras opções de metadados, como AffectsMeasure, você deve marcá-la como pela sobrescrita de seus metadados. Para obter mais informações sobre a substituição ou obtenção de metadados de propriedade, consulte Metadados de Propriedade de Dependência.
Pode ser mais eficiente fazer com que um manipulador de alteração de propriedade invalide a medida, organize e renderize passadas manualmente se nem todas as alterações de propriedades, na verdade, afetam medida, organização e rendering. Por exemplo, você pode decidir re-renderizar um plano de fundo apenas quando um valor for maior que um limite definido. Nesse caso, seu manipulador de alteração de propriedade apenas invalidaria o rendering quando o valor excedesse o limite definido.
Fazer um DependencyProperty Herdadáve não é livre
Por padrão, propriedades de dependência registradas são não herdáveis. No entanto, você pode explicitamente tornar qualquer propriedade herdável. Embora esse seja um recurso útil, converter uma propriedade para ser herdável afeta o desempenho aumentando o período de tempo para a invalidação da propriedade.
Usar RegisterClassHandler cuidadosamente
Enquanto chamada RegisterClassHandler permite que você salve o estado da instância, é importante estar ciente que o manipulador é chamado em cada instância, o que pode causar problemas de desempenho. Use RegisterClassHandler somente quando o aplicativo requer que você salve o estado da instância.
Defina o valor padrão para uma DependencyProperty durante registro
Ao criar uma DependencyProperty que requer um valor padrão, defina o valor usando os metadados padrão passados como um parâmetro ao método Register do DependencyProperty. Use esta técnica em vez de configurar o valor da propriedade em um construtor ou em cada instância de um elemento.
Definir o valor PropertyMetadata usando Registrador
Ao criar uma DependencyProperty, você tem a opção de configurar o PropertyMetadata usando tanto o método Register quanto o método OverrideMetadata. Embora o objeto possa ter um construtor estático para chamar OverrideMetadata, esta não é a solução ideal e afetará o desempenho. Para melhor desempenho, defina a PropertyMetadata durante a chamada a Register.
Objetos Congeláveis
A Freezable é um tipo especial de objeto que tem dois estados: descongeladas e congeladas. Congelar objetos sempre que possível melhora o desempenho do seu aplicativo e reduz seu conjunto de trabalho. Para obter mais informações, consulte Visão geral sobre objetos Freezable.
Cada Freezable possui um evento Changed que é gerado sempre que ele for alterado. No entanto, as notificações de alteração são custosas em termos de desempenho do aplicativo.
Considere o exemplo a seguir em que cada Rectangle usa o mesmo objeto Brush:
rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
Por padrão, WPF fornece um manipulador de eventos para o evento Changed do objeto SolidColorBrush para invalidar a propriedade Fill do objeto Rectangle. Nesse caso, sempre que o SolidColorBrush tem que acionar seu evento Changed é necessário chamar o função de callback para cada Rectangle — o acúmulo desses invocações de função de callback impõe uma penalidade de desempenho significativa. Além disso, é muito intenso em termos de desempenho adicionar e remover manipuladores neste momento porque o aplicativo teria que percorrer a lista inteira para fazer isso. Se seu cenário de aplicativo nunca mudar o SolidColorBrush, você estará pagando o custo de manter os manipuladores de eventos Changed desnecessariamente.
Congelar uma Freezable pode melhorar seu desempenho, porque ele não precisa mais gastar recursos em manter as notificações de alteração. A tabela abaixo mostra o tamanho de um simples SolidColorBrush quando sua propriedade IsFrozen é definida como true, comparado a quando ela não é. Isso pressupõe aplicação de um pincel para a propriedade Fill de dez objetos Rectangle.
Estado |
Size (Tamanho) |
---|---|
SolidColorBrush Congelados |
212 Bytes |
SolidColorBrush Não congelados |
972 Bytes |
O exemplo de código a seguir demonstra esse conceito:
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;
}
Manipuladores alterados em Objetos Congeláveis mas Não congelados podem manter objetos ativos
O representante que um objeto passa o evento Changed de um objeto Freezable é efetivamente uma referência ao objeto. Portanto, os manipuladores de eventos Changed podem manter objetos ativos mais do que o esperado. Ao executar limpeza de um objeto que tenha sido registrado para escutar o evento Changed de um objeto Freezable, é essencial remover esse representante antes de liberar o objeto.
WPF também interliga eventos Changed internamente. Por exemplo, todas as propriedades de dependência que levam Freezable como valor escutarão eventos Changed automaticamente. A propriedade Fill, que usa um Brush, ilustra este conceito.
Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
Na atribuição de myBrush a myRectangle.Fill, um representante apontando de volta para o objeto Rectangle será adicionado ao evento Changed do objeto SolidColorBrush. Isso significa que o código a seguir na verdade não faz myRect qualificado para coleta de lixo:
myRectangle = null;
Nesse caso, myBrush ainda está mantendo myRectangle ativo e irá chamá-lo de volta quando ele acionar o evento Changed. Observe que atribuir myBrush à propriedade Fill de um novo Rectangle simplesmente adicionará outro manipulador de eventos para myBrush.
A maneira recomendada para limpar esses tipos de objetos é remover a Brush da propriedade Fill, que por sua vez removerá o manipulador de eventos Changed.
myRectangle.Fill = null;
myRectangle = null;
Virtualização de interface do usuário
WPF also provides a variation of the StackPanel element that automatically "virtualizes" data-bound child content. In this context, the word virtualize refers to a technique by which a subset of UIElements are generated from a larger number of data items based upon which items are visible on-screen. It is intensive, both in terms of memory and processor, to generate a large number of UI elements when only a few may be on the screen at a given time. VirtualizingStackPanel (through functionality provided by VirtualizingPanel) calculates visible items and works with the ItemContainerGenerator from an ItemsControl (such as ListBox or ListView) to only create UIElements for visible items.
Como uma otimização de desempenho, objetos visuais para esses itens são apenas gerados ou mantidos ativos se eles estiverem visíveis na tela. Quando eles não estão mais na área visível do controle, os objetos visuais podem ser removidos. Isso não deve ser confundido com virtualização de dados, em que objetos de dados não estão todos presentes na coleção local - em vez disso, são transmitidos conforme necessário.
A tabela abaixo mostra o tempo decorrido para adicionar e renderizar 5000 elementos TextBlock a um StackPanel e um VirtualizingStackPanel. Nesse cenário, as medidas representam a hora entre anexar uma sequência de caracteres de texto à propriedade ItemsSource de um objeto ItemsControl até a hora quando os elementos do painel exibem a sequência de texto.
Painel de host |
Tempo de renderização (ms) |
---|---|
3210 |
|
46 |
Consulte também
Conceitos
Optimizing WPF Application Performance
Planejando para desempenho de aplicativos
Otimizando o desempenho: Levando vantagens de hardware
Otimizando o desempenho: Layout and Design
Otimizando o desempenho: 2D Graphics and Imaging
Otimizando o desempenho: Recursos do aplicativo
Otimizando o desempenho: Texto
Otimizando o desempenho: Ligação de Dados