Speicherleck bei der Verwendung von SharedResourceDictionary
wenn Sie arbeitete einige größere wpf-Anwendungen, die Sie möglicherweise vertraut mit diese. Da ResourceDictionaries immer instanziiert werden, jedesmal, wenn Sie gefunden werden, in einer XAML-wir könnten am Ende mit einem resource dictionary mehrfach im Speicher. Also die oben genannte Lösung scheint eine sehr gute alternative. In der Tat für unser Aktuelles Projekt dieser trick hat ... viel Speicher Verbrauch von 800 MB runter zu 44mb, das ist eine wirklich gewaltige Auswirkungen. Leider ist diese Lösung kommt zu einem Preis, dem möchte ich hier zeigen, und hoffentlich einen Weg finden, es zu vermeiden, während immer noch die SharedResourceDictionary
.
Machte ich ein kleines Beispiel zu visualisieren, das problem mit einer shared resource dictionary.
Nur das erstellen einer einfachen WPF-Anwendung. Fügen Sie eine Ressource Xaml
Geteilt.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="myBrush" Color="Yellow"/>
</ResourceDictionary>
Nun fügen Sie ein Benutzersteuerelement. Der codebehind ist nur die default, also habe ich einfach nur zeigen, die xaml -
MyUserControl.xaml
<UserControl x:Class="Leak.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SharedResourceDictionary="clr-namespace:Articy.SharedResourceDictionary" Height="128" Width="128">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Leak;component/Shared.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Rectangle Fill="{StaticResource myBrush}"/>
</Grid>
</UserControl>
Den Fenster-code hinter so aussieht
Window1.xaml.cs
//[ ... ]
public Window1()
{
InitializeComponent();
myTabs.ItemsSource = mItems;
}
private ObservableCollection<string> mItems = new ObservableCollection<string>();
private void OnAdd(object aSender, RoutedEventArgs aE)
{
mItems.Add("Test");
}
private void OnRemove(object aSender, RoutedEventArgs aE)
{
mItems.RemoveAt(mItems.Count - 1);
}
Und das Fenster in xaml wie diese
Window1.xaml
<Window.Resources>
<DataTemplate x:Key="myTemplate" DataType="{x:Type System:String}">
<Leak:MyUserControl/>
</DataTemplate>
</Window.Resources>
<Grid>
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="Add" Click="OnAdd"/>
<Button Content="Remove" Click="OnRemove"/>
</StackPanel>
<TabControl x:Name="myTabs" ContentTemplate="{StaticResource myTemplate}">
</TabControl>
</DockPanel>
</Grid>
</Window>
Ich weiß, das Programm ist nicht perfekt und vermutlich erleichtert werden könnte, während aber herauszufinden, ein Weg, um das problem anzeigen, das ist, was ich kam mit. Trotzdem:
Starten Sie diese und prüfen Sie den Speicherverbrauch, wenn Sie einen memory-profiler-dies wird viel einfacher. Fügen Sie (mit dem zeigen Sie es durch einen Klick auf das Register) und entfernen eine Seite und du wirst sehen, alles funktioniert einwandfrei. Nichts leckt.
Jetzt in der UserControl.Resources
Abschnitt wird die SharedResourceDictionary
statt der ResourceDictionary
gehören die Geteilt.xaml. Sie werden sehen, dass die MyUserControl
wird in Erinnerung bleiben, nachdem Sie entfernt einen Seite, und die MyUserControl
im es.
Ich dachte, das passiert alles instanziert ist über XAML-Wandler, Bedienelemente, etc. Merkwürdigerweise ist das nicht geschehen, auf Benutzerdefinierte Steuerelemente. Meine Vermutung ist, weil nichts wirklich instanziiert, auf benutzerdefinierte Steuerelemente, Daten, Vorlagen und so weiter.
Also Erstens, wie können wir es vermeiden? In unserem Fall mit SharedResourceDictionary ist ein muss, aber die Speicherverluste macht es unmöglich, zu verwenden es produktiv.
Den Verlust kann vermieden werden, mithilfe von CustomControls statt Benutzersteuerelemente, das ist nicht immer praktisch. Also, warum sind Benutzersteuerelemente starke verwiesen wird, die von einem ResourceDictionary?
Ich Frage mich, warum noch niemand erlebt, wie ich schon in einer älteren Frage, es scheint, wie wir Ressourcen verwenden Wörterbücher und XAML absolut falsch, sonst Frage ich mich, warum Sie so inefficent.
Ich hoffe, jemand kann etwas Licht auf diese Angelegenheit.
Vielen Dank im Voraus
Nico
- In Ihrem memory-profiler, was für ein Objekt hielt den Verweis auf die MyUserControl Instanz?
- Ich kann mich nicht erinnern, sicher, aber ich bin fast sicher, es war ein ResourceDictionary, sowie in meinem Fall ein SharedResourceDictionary.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich bin nicht ganz sicher, ob dies das Problem behoben. Aber ich hatte ähnliche Probleme mit
ResourceDictionary
verweisen auf Steuerelemente und seine zu tun, faul Hydratation. Hier ist ein post auf Sie. Und dieser code gelöst, meine Fragen:Ich in der gleichen Ausgabe, dass die freigegebene Ressource-Verzeichnissen in einem large-ish WPF-Projekt. Lesen Sie die Quell-Artikel und die Kommentare, die ich eingebaut ein paar Korrekturen an der SharedDirectory Klasse wie vorgeschlagen in die Kommentare, die zu haben scheinen, entfernt die starke Referenz (gespeichert in _sourceUri) und auch die designer richtig funktionieren. Ich habe dein Beispiel getestet und es funktioniert, sowohl im designer als auch MemProfiler erfolgreich Feststellung, keine gehaltenen Referenzen. Ich würde gerne wissen, ob jemand verbessert es noch weiter, aber das ist, was ich werde mit jetzt:
WeakReference
so dassResourceDictionary
Objekte kann von der garbage Collection freigegeben, wenn nicht in Gebrauch. Siehe diesem blog-post für details.IsInDesignMode
-check funktioniert nicht. Irgendwelche Vorschläge?