Bei der Entwicklung unserer Net Factory Windows Store App auf .NET-Basis sind wir auf ein interessantes Detail im Zusammenhang mit der Windows Runtime gestoßen. Obwohl sich die .NET-Welt mit den neuen WinRT-APIs bei der Entwicklung sehr gut kombinieren lässt, funktioniert die Zusammenarbeit der CLR und der WinRT bei der Speicherverwaltung nicht ganz so reibungslos. Eine Beispiel-Projektmappe zur Reproduktion eines Problems mit WeakReferences steht zum Download bereit.
Bei der mit Windows 8 eingeführten Laufzeitumgebung Windows Runtime handelt es sich um eine COM-basierte, native Laufzeitumgebung. Mittels sogenannter Language Projections stehen die APIs der WinRT scheinbar nahtlos verschiedenen Entwicklungs-Plattformen zur Verfügung. So kann zur Erstellung von Windows Store Apps unter anderem auch das .NET-Framework eingesetzt werden. Wie bei klassischen .NET-Anwendungen wird in diesem Fall der IL-Code zur Laufzeit von der CLR ausgeführt. Obwohl bei der Entwicklung die Grenzen zwischen WinRT und .NET kaum sichtbar sind, arbeiten bei der Ausführung der App somit zwei verschiedene Laufzeitumgebungen zusammen.
Dieser Fakt wird besonders deutlich, wenn Funktionen zur Speicherverwaltung zum Einsatz kommen. So ist z. B. bei Verwendung der .NET-Klasse WeakReference in der Nähe von WinRT-APIs äußerste Vorsicht geboten. Denn entdeckt der Garbage Collector der CLR, dass ein WinRT-Objekt vom .NET-Code der Anwendung nicht mehr referenziert wird, gibt er es an die WinRT frei. War dieses Objekt das Zielobjekt einer WeakReference, so gilt diese als “tot”, obwohl das Objekt ggfs. von WinRT noch verwendet wird. Insbesondere bei der Benutzung des WinRT-XAML-Frameworks existieren allerdings zahlreiche Objekte innerhalb der Windows Runtime, welche häufig von Managed Code verwendet werden. Werden WeakReferences eingesetzt, um diese Objekte (i. d. R. Steuerelemente) effizient zu verwalten; kann es leicht zu dieser bizarren Situation kommen: Das Objekt ist zwar auf der grafischen Oberfläche noch vorhanden und kann ggfs. sogar mit dem Benutzer interagieren, ist vom .NET-Code aus allerdings nicht mehr erreichbar.
Wir entdeckten diese Eigenheit des Zusammenspiels aus WinRT und .NET beim Einsatz eines inoffiziellen Prism-Ports für die Windows-Store-Plattform. Dessen RegionManager verwendet bei der Deklaration von Regionen WeakReferences, um sich beim Setzen der RegionName-Eigenschaft das entsprechende Steuerelement zu merken. Bei der Erstellung der Regionen ruft das Prism-Framework die zugehörigen Steuerelemente über die schwachen Referenzen wieder ab. Da der Managed Code der Anwendung in der Zwischenzeit allerdings keinerlei starke Referenzen mehr auf das Steuerelement hatte, führten die WeakReferences unter Umständen ins Leere. Dieses Verhalten beobachteten wir erst, als wir die Anwendung auf einem Microsoft Surface RT ausführten, welches im Vergleich zur Entwicklungsmaschine mit einem wesentlich kleineren Hauptspeicher ausgestattet ist.
Als Workaround verwenden wir nun benannte Steuerelemente, die dadurch vom Managed-Code-Teil des zugehörigen User Controls referenziert werden. So ist sichergestellt, dass das Objekt an die Lebenszeit des User Controls gekoppelt ist, so dass die WeakReference das Objekt nicht verliert, so lange sie es noch benötigt, es aber auch nicht unnötig lange im Speicher gehalten wird.
Um das Problem zu reproduzieren, können Sie sich unsere Beispiel-Projektmappe herunterladen. Mit den enthaltenen Minimalbeispielen kann gezeigt werden, dass das beschriebene Verhalten lediglich auf der Windows-Store-Plattform auftritt, nicht aber in WPF oder Silverlight.
Wir sind gespannt, wie das patterns & practices Team diese Situation im kommenden offiziellen Release des Prism-Frameworks für Windows Store Apps lösen wird.