Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Wanneer u Win2D-besturingselementen gebruikt in beheerde XAML-toepassingen, moet u ervoor zorgen dat er geen referentiecycli ontstaan die kunnen voorkomen dat deze besturingselementen door de garbagecollector worden vrijgemaakt.
U hebt een probleem als...
- U gebruikt Win2D vanuit een .NET-taal zoals C# (niet systeemeigen C++)
- U gebruikt een van de Win2D XAML-besturingselementen:
- U abonneert u op gebeurtenissen van het Besturingselement Win2D (bijvoorbeeld
Draw,CreateResources,SizeChanged...) - Uw app wordt heen en weer verplaatst tussen meer dan één XAML-pagina
Als aan al deze voorwaarden wordt voldaan, verhindert een referentietelling cyclus dat het Win2D-besturingselement ooit wordt verzameld door de vuilnisopruimer. Nieuwe Win2D-resources worden telkens toegewezen wanneer de app naar een andere pagina wordt verplaatst, maar de oude resources worden nooit vrijgemaakt zodat er geheugen wordt gelekt. Om dit te voorkomen, moet u code toevoegen om de cyclus expliciet te verbreken.
Het probleem oplossen
Als je de cyclus voor het aantal verwijzingen wilt doorbreken en je pagina door de garbage collector wilt laten verzamelen:
- Sluit de
Unloadedgebeurtenis aan van de XAML-pagina die het Win2D-besturingselement bevat - Roep in de
Unloaded-handlerRemoveFromVisualTreeaan op het Win2D-besturingselement - Laat in de
Unloadedhandler eventuele expliciete verwijzingen naar het Win2D-besturingselement los (door deze in te stellen opnull)
Voorbeeldcode:
void page_Unloaded(object sender, RoutedEventArgs e)
{
this.canvas.RemoveFromVisualTree();
this.canvas = null;
}
Zie een van de demopagina's van de voorbeeldgalerie voor werkvoorbeelden.
Hoe te testen op lekken in de cyclus
Als u wilt testen of uw toepassing refcount-cycli correct onderbreekt, voegt u een finalizer-methode toe aan XAML-pagina's die Win2D-besturingselementen bevatten:
~MyPage()
{
System.Diagnostics.Debug.WriteLine("~" + GetType().Name);
}
Stel in uw App constructor een timer in die ervoor zorgt dat garbage collection regelmatig wordt uitgevoerd.
var gcTimer = new DispatcherTimer();
gcTimer.Tick += (sender, e) => { GC.Collect(); };
gcTimer.Interval = TimeSpan.FromSeconds(1);
gcTimer.Start();
Navigeer naar de pagina en ga er vervolgens vandaan naar een andere pagina. Als alle cycli zijn verbroken, ziet u binnen een of twee seconden Debug.WriteLine uitvoer in het uitvoervenster van Visual Studio.
Houd er rekening mee dat het aanroepen GC.Collect verstorend is en de prestaties pijn doet, dus u moet deze testcode verwijderen zodra u klaar bent met testen op lekken.
De gruwelijke details
Een cyclus treedt op wanneer een object A een verwijzing naar B heeft, op hetzelfde moment als B ook een verwijzing naar A heeft. Of wanneer A verwijst naar B en B verwijst naar C, terwijl C verwijst naar A, enzovoort.
Wanneer u zich abonneert op gebeurtenissen van een XAML-besturingselement, is dit soort cyclus vrijwel onvermijdelijk:
- Een XAML-pagina bevat verwijzingen naar alle controls binnen het document.
- Besturingselementen bevatten verwijzingen naar de handler-gemachtigden die zijn geabonneerd op hun gebeurtenissen
- Iedere gedelegeerde bevat een verwijzing naar zijn doelinstantie.
- Evenementhandlers zijn meestal instantiemethoden van de XAML-pagina-klasse, zodat hun doelinstanties terug verwijzen naar de XAML-pagina, waardoor een cyclus wordt gecreëerd.
Als alle betrokken objecten in .NET worden geïmplementeerd, zijn dergelijke cycli geen probleem omdat .NET "garbage collection" gebruikt en het garbage collection-algoritme in staat is groepen objecten te identificeren en vrij te maken, zelfs als ze in een cyclus gekoppeld zijn.
In tegenstelling tot .NET beheert C++ het geheugen op basis van het tellen van verwijzingen, waardoor cycli van objecten niet kunnen worden gedetecteerd en vrijgemaakt. Ondanks deze beperking hebben C++-apps die gebruikmaken van Win2D geen probleem omdat C++-gebeurtenishandlers in plaats van sterke, zwakke verwijzingen naar hun doelinstanties bevatten. De pagina verwijst daarom naar het besturingselement en het besturingselement verwijst naar de delegate van de gebeurtenishandler, maar deze delegate verwijst niet terug naar de pagina, waardoor er geen cyclus ontstaat.
Het probleem treedt op wanneer een C++ WinRT-onderdeel, zoals Win2D, wordt gebruikt door een .NET-toepassing:
- De XAML-pagina maakt deel uit van de toepassing, dus maakt gebruik van garbage collection.
- Het Win2D-besturingselement wordt geïmplementeerd in C++ en gebruikt referentietelling.
- De gedelegeerde gebeurtenishandler maakt deel uit van de toepassing, dus maakt gebruik van vuilnisophaling en bevat een sterke verwijzing naar het doelexemplaar.
Er is een cyclus aanwezig, maar de Win2D-objecten die aan deze cyclus deelnemen, maken geen gebruik van .NET garbagecollection. Dit betekent dat de garbagecollector de hele keten niet kan zien, zodat de objecten niet kunnen worden gedetecteerd of vrijgemaakt. Wanneer dit gebeurt, moet de toepassing helpen door de cyclus expliciet te verbreken. U kunt dit doen door alle verwijzingen van de pagina naar het besturingselement uit te brengen (zoals hierboven wordt aanbevolen) of door alle verwijzingen van het besturingselement naar gedelegeerden van de gebeurtenis-handler uit te brengen die naar de pagina kunnen verwijzen (met behulp van de gebeurtenis Niet-geladen pagina om alle gebeurtenis-handlers af te melden).
Windows developer