I was running a memory profile on our client application and kept seeing views being stuck in memory. It was odd since we freed them and there weren’t any discernable links that should be keeping them alive.
Well, that is until I saw that a GC Root object holding a reference at the top of the chain was an icon. An Icon. It was holding together a massive view and all the data, controls and containers that go with it. The memory would increase by 5-10 megs each time you went and left the view.
So, my next step was to figure out how in the world this icon was holding the view in memory. Where was the reference? The only usage was a simple binding to show the icon for particular types of nodes in our treeview.
I remembered from an earlier performance run that WPF has Freezable objects. WPF monitors these objects for changes. Well, that sounds like it uses an event. I doubt it polls.
I was right. There is an event on the image source. Apparently WPF latches on to that bad boy, and voila. You have a valid reference to the icon that the GC see as needed.
Why needed, well, to save cycles, we use a static instantiation of the bitmap. Loading from resources is expensive, so we use a readonly static. It never goes anywhere, therefore anything bound to its events doesn’t go anywhere.
So, what is the fix? I don’t want to stop using my static, as now I introduce a performance penalty. I can’t stop using the icon, then the user would be perturbed.
What to do?
Well, bring out your ice ray gun, because the solution is to Freeze the image. I mentioned earlier that WPF has Freezable objects. Well, if you Freeze it, then WPF doesn’t bind to the event since the object can’t change. So, a few lines of code later and we have a memory leak disaster averted.
Here was the old way:
private static readonly ImageSource MyIcon = new BitmapImage(new Uri("pack://application:,,,/Seekford.Client.Resources;component/Images/Icon.png"));
Here is the new way:
private static readonly ImageSource MyIcon;
MyIcon = new BitmapImage(new Uri("pack://application:,,,/Seekford.Client.Resources;component/Images/Icon.png"));
Simple, right. Yeah, if you know what to look for in the first place