Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Wenn Sie Win2D-Steuerelemente in verwalteten XAML-Anwendungen verwenden, müssen Sie darauf achten, dass Verweisanzahlzyklen vermieden werden, die verhindern können, dass diese Steuerelemente vom Garbage Collector eingesammelt werden.
Sie haben ein Problem, wenn...
- Sie verwenden Win2D aus einer .NET-Sprache wie C# (nicht natives C++)
- Sie verwenden eines der Win2D-XAML-Steuerelemente:
- Sie abonnieren Ereignisse des Win2D-Steuerelements (zum Beispiel
Draw,CreateResources,SizeChanged...) - Ihre App wechselt zwischen mehr als einer XAML-Seite hin und her
Wenn alle diese Bedingungen erfüllt sind, verhindert ein Referenzzählerzyklus, dass das Win2D-Steuerelement jemals der Speicherbereinigung zum Opfer fällt. Neue Win2D-Ressourcen werden jedes Mal zugewiesen, wenn die App zu einer anderen Seite wechselt, aber die alten werden nie freigegeben, sodass Arbeitsspeicher verloren geht. Um dies zu vermeiden, müssen Sie Code hinzufügen, um den Zyklus explizit zu unterbrechen.
So beheben Sie es
Um den Referenzzählzyklus zu unterbrechen und zu erlauben, dass Ihre Seite von der Speicherbereinigung aufgenommen wird:
- Hängen Sie das
Unloaded-Ereignis der XAML-Seite ein, die das Win2D-Steuerelement enthält. - Rufen Sie im
Unloaded-HandlerRemoveFromVisualTreefür das Win2D-Steuerelement auf. - Geben Sie im
Unloaded-Handler alle expliziten Verweise auf das Win2D-Steuerelement frei (durch Festlegen aufnull)
Beispielcode:
void page_Unloaded(object sender, RoutedEventArgs e)
{
this.canvas.RemoveFromVisualTree();
this.canvas = null;
}
Arbeitsbeispiele finden Sie auf einer beliebigen Demoseite der Beispielgalerie.
So testen Sie auf Lecks im Kreislauf
Um zu testen, ob Ihre Anwendung Refcount-Zyklen ordnungsgemäß unterbricht, fügen Sie allen XAML-Seiten, die Win2D-Steuerelemente enthalten, eine Finalizer-Methode hinzu.
~MyPage()
{
System.Diagnostics.Debug.WriteLine("~" + GetType().Name);
}
Richten Sie in Ihrem App-Konstruktor einen Timer ein, der sicherstellt, dass die Garbage Collection in regelmäßigen Abständen erfolgt:
var gcTimer = new DispatcherTimer();
gcTimer.Tick += (sender, e) => { GC.Collect(); };
gcTimer.Interval = TimeSpan.FromSeconds(1);
gcTimer.Start();
Navigieren Sie zu der Seite, und wechseln Sie dann zu einer anderen Seite. Wenn alle Zyklen unterbrochen wurden, wird die Ausgabe Debug.WriteLine innerhalb von ein bis zwei Sekunden im Visual Studio-Ausgabebereich angezeigt.
Beachten Sie, dass das Aufrufen GC.Collect störend ist und die Leistung beeinträchtigt, daher sollten Sie diesen Testcode entfernen, sobald Sie den Test für Lecks abgeschlossen haben!
Die blutigen Details
Ein Zyklus tritt auf, wenn ein Objekt A einen Verweis auf B hat, während B auch einen Verweis auf A hat. Oder wenn A auf B verweist und B auf C verweist, während C auf A verweist, usw.
Beim Abonnieren von Ereignissen eines XAML-Steuerelements ist so ein Zyklus ziemlich unvermeidlich.
- XAML-Seite enthält Verweise auf alle darin enthaltenen Steuerelemente.
- Steuerelemente enthalten Verweise auf die Handlerdelegats, die ihre Ereignisse abonniert haben
- Jeder Delegate hält einen Verweis auf seine Zielinstanz.
- Ereignishandler sind in der Regel Instanzmethoden der XAML-Seitenklasse, sodass ihre Zielinstanzverweise auf die XAML-Seite verweisen und einen Zyklus bilden.
Wenn alle beteiligten Objekte in .NET implementiert sind, sind solche Zyklen kein Problem, da .NET über eine Speicherbereinigung verfügt. Der Speicherbereinigungs-Algorithmus ist in der Lage, Gruppen von Objekten zu identifizieren und freizugeben, auch wenn sie in einem Zyklus verknüpft sind.
Im Gegensatz zu .NET verwaltet C++ den Speicher durch Referenzzählung, die keine Objektzyklen erkennen und auflösen kann. Trotz dieser Einschränkung haben C++-Apps, die Win2D verwenden, kein Problem, da C++-Ereignishandler standardmäßig schwache und keine starken Verweise auf ihre Zielinstanz enthalten. Daher verweist die Seite auf das Steuerelement, und das Steuerelement verweist auf den Ereignishandlerdelegaten, aber dieser Delegat verweist nicht zurück auf die Seite, sodass es keinen Zyklus gibt.
Das Problem tritt auf, wenn eine C++-WinRT-Komponente wie Win2D von einer .NET-Anwendung verwendet wird:
- Die XAML-Seite ist Teil der Anwendung, verwendet daher die Garbage Collection.
- Das Win2D-Steuerelement wird in C++ implementiert, daher erfolgt die Referenzzählung.
- Der Ereignishandlerdelegat ist Teil der Anwendung, verwendet also garbage collection und enthält einen starken Verweis auf seine Zielinstanz.
Ein Zyklus ist vorhanden, aber die win2D-Objekte, die an diesem Zyklus teilnehmen, verwenden keine .NET Garbage Collection. Dies bedeutet, dass der Garbage Collector nicht die gesamte Kette sehen kann, sodass er die Objekte nicht erkennen oder freigeben kann. In diesem Fall muss die Anwendung helfen, indem der Zyklus explizit abgebrochen wird. Dazu können Sie entweder alle Verweise von der Seite auf das Steuerelement freigeben (wie oben empfohlen) oder alle Verweise vom Steuerelement auf Ereignishandlerdelegaten freigeben, die möglicherweise auf die Seite zurückverweisen (indem Sie das Unloaded-Ereignis der Seite verwenden, um alle Ereignishandler abzubestellen).
Windows developer