Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Ao usar controles Win2D em aplicativos XAML gerenciados, deve-se tomar cuidado para evitar ciclos de contagem de referência que possam impedir que esses controles sejam recuperados pelo coletor de lixo.
Você tem um problema se...
- Você está usando Win2D de uma linguagem .NET, como C# (não C++ nativo)
- Você usa um dos controles Win2D XAML:
- Você subscreve-se nos eventos do controle Win2D (por exemplo,
Draw,CreateResources,SizeChanged...) - Seu aplicativo se move para frente e para trás entre mais de uma página XAML
Se todas essas condições forem atendidas, um ciclo de contagem de referência impedirá que o controle Win2D seja alguma vez recolhido pelo coletor de lixo. Novos recursos do Win2D são alocados cada vez que o aplicativo se move para uma página diferente, mas os antigos nunca são liberados para que a memória seja vazada. Para evitar isso, você deve adicionar código para quebrar explicitamente o ciclo.
Como corrigir
Para quebrar o ciclo de contagem de referência e deixar sua página ser coletada como lixo:
- Conecte o evento
Unloadedda página XAML que contém o controle Win2D - No manipulador de
Unloaded, chameRemoveFromVisualTreeno controle Win2D -
UnloadedNo manipulador, libere (definindo comonull) quaisquer referências explícitas ao controle Win2D
Código de exemplo:
void page_Unloaded(object sender, RoutedEventArgs e)
{
this.canvas.RemoveFromVisualTree();
this.canvas = null;
}
Para exemplos práticos, consulte qualquer uma das páginas de demonstração da Galeria de Exemplos.
Como testar a existência de fugas no ciclo
Para testar se seu aplicativo está quebrando corretamente os ciclos de refcount, adicione um método finalizador a qualquer página XAML que contenha controles Win2D:
~MyPage()
{
System.Diagnostics.Debug.WriteLine("~" + GetType().Name);
}
Em seu App construtor, configure um temporizador que garantirá que a coleta de lixo ocorra em intervalos regulares:
var gcTimer = new DispatcherTimer();
gcTimer.Tick += (sender, e) => { GC.Collect(); };
gcTimer.Interval = TimeSpan.FromSeconds(1);
gcTimer.Start();
Navegue até a página e, depois, vá para outra página. Se todos os ciclos tiverem sido quebrados, verá a saída Debug.WriteLine no painel de saída do Visual Studio dentro de um ou dois segundos.
Observe que a chamada GC.Collect é perturbadora e prejudica o desempenho, então você deve remover esse código de teste assim que terminar de testar vazamentos!
Os detalhes sangrentos
Um ciclo ocorre quando um objeto A tem uma referência a B, ao mesmo tempo que B também tem uma referência a A. Ou quando A faz referência a B, e B faz referência a C, enquanto C faz referência a A, etc.
Ao assinar eventos de um controle XAML, esse tipo de ciclo é praticamente inevitável:
- A página XAML contém referências a todos os controles contidos nela
- Os controles contêm referências aos delegados do manipulador que foram inscritos em seus eventos
- Cada delegado contém uma referência à sua instância de destino
- Os manipuladores de eventos geralmente são métodos de instância da classe de página XAML, portanto, suas referências de instância de destino apontam de volta para a página XAML, criando um ciclo
Se todos os objetos envolvidos forem implementados em .NET, esses ciclos não serão um problema porque o .NET possui um sistema de coleta de lixo, e o algoritmo de coleta de lixo é capaz de identificar e recuperar grupos de objetos, mesmo que estejam ligados em um ciclo.
Ao contrário do .NET, o C++ gerencia a memória por contagem de referência, que é incapaz de detetar e recuperar ciclos de objetos. Apesar dessa limitação, os aplicativos C++ que usam Win2D não têm problema porque os manipuladores de eventos C++ usam como padrão manter referências fracas em vez de fortes para sua instância de destino. Portanto, a página faz referência ao controlo e o controlo faz referência ao delegado do manipulador de eventos, mas não há ciclo porque esse delegado não faz referência de volta à página.
O problema ocorre quando um componente C++ WinRT, como Win2D, é usado por um aplicativo .NET:
- A página XAML faz parte do aplicativo, portanto, usa a coleta de lixo
- O controle Win2D é implementado em C++, portanto, usa a contagem de referência
- O delegado do manipulador de eventos faz parte do aplicativo e por isso usa a coleta de lixo e mantém uma referência forte à sua instância de destino
Um ciclo está presente, mas os objetos Win2D que participam desse ciclo não estão usando a coleta de lixo .NET. Isso significa que o coletor de lixo é incapaz de ver toda a cadeia, portanto, não pode detetar ou recuperar os objetos. Quando isso ocorre, a aplicação deve intervir quebrando explicitamente o ciclo. Isso pode ser feito liberando todas as referências da página para o controle (conforme recomendado acima) ou liberando todas as referências do controle para delegados do manipulador de eventos que possam apontar de volta para a página (usando o evento page Unloaded para cancelar a inscrição de todos os manipuladores de eventos).
Windows developer