I'm working with large amount of objects (POI's) that are getting displayed on a MapControl
. I'm helping myself with a MVVM Light to obey the rules of the MVVM approach.
Since I'm obligated to display every object on the map, I have to use MapItemsControl
collection, and not the MapElements
one.
This collection binds to the the ObservableCollection<PushpinViewModel>
object (Pushpins
) in corresponding ViewModel
. Everything works as expected, up to the point, when I want to refresh Pushpins
. The problem is memory leak. But first, some 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; }
/* Ctor, initialization of Pushpins and stuff like that */
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 add to the Pushpins
collection 1000 PushpinViewModel
elements. They are rendered, memory is allocated, everything's fine. Now I want to clear the collection, and add another (different in real scenario) 1000 elements. So, I call Clear()
method. But.. nothing happens! Pushpins
gets cleared, but PushpinViewModel
's finalizers are not called! Then I add 1000 elements again, and my memory usage doubles.
You can guess what happens next. When I repeat this Clear()
- Add()
procedure 3-5 times my app crashes.
So, what is the problem? Clearly ObservableCollection
is holding references to the PushpinViewModel
objects after Clear()
has been performed on it, so they cannot be garbage collected. Of course forcing GC to perform garbage collection does not help (it sometimes even makes the situation worse).
It's bothering me for 2 days now, I have tried many different scenarios to try and overcome this problem, but to be honest, nothing helped.
There was only one thing worth nothing - I don't remember the exact scenario, but when I had assigned Pushpins = null
, and then did something more, VehiceViewModel
's were destroyed. But that does not work for me, because I also remember that I had problem with visualizing these pins on the map after the Clear()
.
Do you have any ideas what can cause this memory leak? How can I force OC
's members to destroy? Maybe there is some kind of alternative for OC
?
Thanks in advance for any help!
EDIT:
I did some tests with XAML Map Control - https://xamlmapcontrol.codeplex.com/, and results are surprising. Overall map performance with >1000 elements added to it, is poorer than a native MapControl
, BUT, if I call Add()
x1000, then Clear()
, then Add()
x1000, the PushpinViewModel
's finalizers are geting called! Memory gets freed, and app does not crash. So there is definitely something wrong with Microsoft's MapControl
...