I work with a large number of objects (POIs) that are displayed on MapControl . I help myself with MVVM Light obey the rules of the MVVM approach.
Since I must display every object on the map, I need to use the MapItemsControl collection, not MapElements . This collection is bound to the ObservableCollection<PushpinViewModel> ( Pushpins ) Pushpins in the corresponding ViewModel . Everything works as expected, until the moment when I want to update Pushpins . The problem is a memory leak. But first, the code to visualize the problem:
XAML:
<maps:MapControl x:Name="Map" x:Uid="MapControl"> <maps:MapItemsControl ItemsSource="{Binding Pushpins}"> <maps:MapItemsControl.ItemTemplate> <DataTemplate> <Image Source="{Binding Image}"/> </DataTemplate> </maps:MapItemsControl.ItemTemplate> </maps:MapItemsControl>
MainViewModel:
public class MainViewModel : ViewModelBase { public RelayCommand AddCommand { get; set; } public RelayCommand ClearCommand { get; set; } public RelayCommand CollectCommand { get; set; } public ObservableCollection<PushpinViewModel> Pushpins { get; set; } private void Collect() { GC.Collect(2); GC.WaitForPendingFinalizers(); GC.Collect(2); PrintCurrentMemory(); } private void Clear() { Pushpins.Clear(); PrintCurrentMemory(); } private void Add() { for (int i = 0; i < 1000; i++) { Pushpins.Add(new PushpinViewModel()); } PrintCurrentMemory(); } private void PrintCurrentMemory() { Logger.Log(String.Format("Total Memory: {0}", GC.GetTotalMemory(true) / 1024.0)); } }
PushpinViewModel:
public class PushpinViewModel: ViewModelBase { public string Image { get { return "/Assets/SomeImage.png"; } } ~PushpinViewModel() { Logger.Log("This finalizer never gets called!"); } }
Now consider the following scenario. I am adding to the elements of the collection Pushpins 1000 PushpinViewModel . They are visualized, memory is allocated, everything is fine. Now I want to clear the collection and add another (another in the real scenario) 1000 elements. So, I call the Clear() method. But ... nothing happens! Pushpins cleared, but PushpinViewModel finalizers are not called! Then I add 1000 elements again and the memory usage is doubled. You can guess what will happen next. When I repeat this procedure Clear() - Add() 3-5 times, my application crashes.
So what is the problem? Obviously, the ObservableCollection contains references to PushpinViewModel objects after executing Clear() , so they cannot be garbage collected. Of course, forcing the GC to collect garbage does not help (sometimes it even makes the situation worse).
This bothers me for 2 days, I tried many different scenarios to try to overcome this problem, but, frankly, nothing helped. Nothing was worth it - I donβt remember the exact script, but when I assigned Pushpins = null and then did something else, the VehiceViewModel was destroyed. But this does not work for me, because I also remember that I had a problem visualizing these contacts on the map after Clear() .
Do you have any idea what might cause a memory leak? How can I get OC members to be destroyed? Maybe there is some alternative for OC ? Thanks in advance for your help!
EDIT:
I did some tests using XAML Map Control - https://xamlmapcontrol.codeplex.com/ , and the results are surprising. The overall performance of the map with the addition of 1000 elements to it is worse than the native MapControl , BUT, if I call Add() x1000, then Clear() , then Add() x1000, the PushpinViewModel finalizers will get a call! Memory is freed and the application does not crash. So with Microsoft MapControl ... there is definitely something wrong ...