Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Lorsque vous utilisez des contrôles Win2D dans des applications XAML managées, vous devez veiller à éviter les cycles de nombre de références qui pourraient empêcher ces contrôles d’être récupérés par le garbage collector.
Vous avez un problème si...
- Vous utilisez Win2D à partir d’un langage .NET tel que C# (pas C++natif)
- Vous utilisez l’un des contrôles XAML Win2D :
- Vous vous abonnez aux événements du contrôle Win2D (par exemple
Draw, ,CreateResourcesSizeChanged...) - Votre application navigue entre plusieurs pages XAML
Si toutes ces conditions sont remplies, un cycle de comptage de références empêchera le contrôle Win2D d'être jamais collecté par le système de gestion de la mémoire. Les nouvelles ressources Win2D sont allouées chaque fois que l'application passe à une page différente, mais les anciennes ne sont jamais libérées, ce qui entraîne une fuite de mémoire. Pour éviter cela, vous devez ajouter du code pour rompre explicitement le cycle.
Procédure de résolution
Pour interrompre le cycle de comptage des références et permettre la récupération de votre page par le ramasse-miettes :
- Accrochez l’événement
Unloadedde la page XAML qui contient le contrôle Win2D - Dans le gestionnaire
Unloaded, appelerRemoveFromVisualTreesur le contrôle Win2D - Dans le
Unloadedgestionnaire, relâchez (en définissant surnull) toutes les références explicites au contrôle Win2D
Exemple de code :
void page_Unloaded(object sender, RoutedEventArgs e)
{
this.canvas.RemoveFromVisualTree();
this.canvas = null;
}
Pour obtenir des exemples pratiques, consultez l'une des pages de démonstration de la Galerie d'exemples .
Comment tester les fuites de cycle
Pour tester si votre application interrompt correctement les cycles de refcount, ajoutez une méthode finaliseur à toutes les pages XAML qui contiennent des contrôles Win2D :
~MyPage()
{
System.Diagnostics.Debug.WriteLine("~" + GetType().Name);
}
Dans votre constructeur App, configurez un minuteur afin de garantir que la collecte des déchets ait lieu à intervalles réguliers.
var gcTimer = new DispatcherTimer();
gcTimer.Tick += (sender, e) => { GC.Collect(); };
gcTimer.Interval = TimeSpan.FromSeconds(1);
gcTimer.Start();
Accédez à la page, puis, de là, accédez à une autre page. Si tous les cycles ont été rompus, vous verrez la sortie Debug.WriteLine dans le volet de sortie de Visual Studio dans une seconde ou deux.
Notez que l’appel GC.Collect est perturbant et nuit aux performances. Vous devez donc supprimer ce code de test dès que vous avez terminé de tester les fuites !
Les détails sanglants
Un cycle se produit lorsqu’un objet A a une référence à B, en même temps que B a également une référence à A. Ou quand A référence B et B référence C, tandis que C fait référence À, etc.
Lors de l’abonnement aux événements d’un contrôle XAML, ce type de cycle est presque inévitable :
- La page XAML contient des références à tous les contrôles qu’elle contient
- Les contrôles contiennent des références aux délégués du gestionnaire qui ont été abonnés à leurs événements
- Chaque délégué contient une référence à son instance cible
- Les gestionnaires d’événements sont généralement des méthodes d’instance de la classe de page XAML. Par conséquent, leurs références d’instance cible pointent vers la page XAML, créant un cycle
Si tous les objets impliqués sont implémentés dans .NET, ces cycles ne sont pas un problème, car .NET est garbage collected, et l’algorithme de garbage collection est en mesure d’identifier et de récupérer des groupes d’objets même s’ils sont liés dans un cycle.
Contrairement à .NET, C++ gère la mémoire en comptant les références, ce qui n’est pas en mesure de détecter et de récupérer des cycles d’objets. Malgré cette limitation, les applications C++ utilisant Win2D n’ont aucun problème, car les gestionnaires d’événements C++ ont la valeur par défaut pour contenir des références faibles plutôt que des références fortes à leur instance cible. Par conséquent, la page fait référence au contrôle et le contrôle fait référence au délégué du gestionnaire d’événements, mais ce délégué ne renvoie pas à la page, éliminant ainsi tout cycle.
Le problème se produit lorsqu’un composant WinRT C++ tel que Win2D est utilisé par une application .NET :
- La page XAML fait partie de l’application, et utilise par conséquent le ramasse-miettes.
- Le contrôle Win2D est implémenté en C++. Utilise donc le comptage de références
- Le délégué du gestionnaire d’événements fait partie de l’application, il utilise donc le ramasse-miettes et contient une référence forte à son instance cible.
Un cycle est présent, mais les objets Win2D participant à ce cycle n'utilisent pas le ramasse-miettes .NET. Cela signifie que le garbage collector ne peut pas voir l’intégralité de la chaîne, de sorte qu’il ne peut pas détecter ou récupérer les objets. Lorsque cela se produit, l’application doit vous aider en cassant explicitement le cycle. Pour ce faire, vous pouvez libérer toutes les références de la page au contrôle (comme recommandé ci-dessus) ou libérer toutes les références du contrôle vers les délégués de gestionnaires d’événements qui peuvent retourner à la page, en utilisant l’événement Unloaded de la page pour désabonner tous les gestionnaires d'événements.
Windows developer