Yesterday my team leader told me he is pretty sure we have a memory leak in our application. The memory usage for the web browser just continued to grow without any end. And when using an environment with a lot of data that problem multiplied. I actually thought we had one after my computer came to a crawl when I forgot to close the browser…
The task manager view on my IE memory:
Well, first of all I found and downloaded Sysinternal’s VMMap, in order to see my memory map:
This window is totally unreadable to me, what about you?
Pressing F5 refreshes the view (and enables the Timeline button).
The colors are set by the middle table:
The bottom table can be played with like a regular grid (Dah!)
Pressing the Timeline button, starting timeline:
Ending timeline:
Something is a little weird here for me, at the bottom there is a view of all the memory usage of my Silverlight Application, in the middle there is just an empty space, and on the top there is the line (pink?) of the current memory usage. My take on this is that my Application is using less than 50MB of memory but has free memory locked in bubbles of used memory (like in the file system where you have Size and Size on Disk, Size on Disk >= Size). It is actually called fragmentation of the large heap you can read more about it here, here and here (the links can also be found at the bottom of this post).
So I decided to look at the Fragmentation View (under View in the menu):
I can see here what I thought happened: the free memory (white) is locked inside used memory.
At this stage in time (~700MB in task manager) my Windows 7 machine became unresponsive and I had to terminate the IE!
Next lets look at Ants Memory Profiler (I am using the trial version):
After installation I got a nice screen to enter my Silverlight application:
I waited for the memory to stabilized and then took a memory Snapshot (the graph in the middle show the memory status):
A summery I can actually follow:
I took another snapshot and this time in the class list I could see the changes of the instances:
I found it useful to sort by Size Diff and Instance Diff.
Well at this point in time I decided to go to lunch and keep the profiler running. When I returned from lunch I noticed the memory spike and decided to take a snapshot – that was a mistake apparently because the computer became unresponsive and I had to kill it (or as I like to call it I had to “suffocate it with my bare hands” by pressing the shut down button until the computer “died” (shutdown) and then I turned it on).
First I decided to record a base line – not to do any action and see how the memory acts. If you haven’t guessed let me give you a hint: it goes up! (let me warn you though having 9 snapshots is 5 too much when saving the data – the computer just slows down and the hard drive is 2GBs less)
The timeline:
(I am just a growing boy…)
Session overview:
(the only graph not in constant growth is Gen1 which means that almost no new objects are created and freed, those that are not freed move to Gen2)
Comparing snapshot 9 to snapshot 1
(25 minutes of hell) seen a growth of ~160MB
Memory Fragmentation:
(why can’t I copy text in Ants?)
Most of the memory goes to the Gen2:
Class list:
It seems the problematic class is CustomDependencyProperty, it also seems that all top three lines are connected (look at the Instance Diff column it has about the same number) and they are also the top rows in all the sorting options. By the way, looking at the top three lines in comparing snapshot 9 and 8 shown the same result (minus around 800K in Diff).
Well Instance Categorizer solved one mystery:
(explains the exact same number of Instance Diff)
With String I found the culprit in Instance List you can see the values of the strings:
(without the 100 at the end of the string these are one of my ArcGIS Server layer’s field names)
It was because of a stupid mistake I did back when I knew nothing of Silverlight and decided to use my knowledge of regular ESRI code with the Silverlight API. I wrapped ESRI Graphic class with my own class, inherited from it and wrapped all the work with Attributes with property – until here all is well (using Attributes with strings is just evil). Then I had trouble with fields that were updated and the updates weren’t shown on the forms and my team leader suggested I use Dependency Properties so I just added it to the entities base class with DependencyProperty.Register and forgot about it till now.
The next bit of information is that we have a mechanism for automatically updating all the Feature Layers once (+/-X seconds (X is determined in the config)) there is a change in the layer.
The third bit of information is that we have a grid that shows updates to two of this layers and uses the wrapper entities to do that (getting the values for the rows/columns of the grid with properties).
Since the dependencies aren’t unregistered and the entities are recreated – the memory just grows and grows.
Current end result
Timeline:
(it is still rising just slower, as you can see now the line is a bit more balanced.)
The size change between snapshot 1 and snapshot 4 is ~680KB (no MBs this time!)
Most of my memory is unmanaged (the same amount from last time):
Session Overview:
(I still don’t like the unused space in the Large Object Heap Size but there is always tomorrow…)
Memory Fragmentation:
(look at that it’s green with “No issues…”)
Class list:
(no millions of objects – where did they go?)
Another sort of class list:
(would you look at that, this time there are different results)
By the way ants also gives advice about what the unmanaged memory could be. You should check it out.
It’s not the end, just the beginning… tomorrow I will check how the application uses the memory when it is run (not just refreshed every 30 seconds).
Continue in part 2
Resources:
Understanding Garbage Collection in .NET
MSDN, CLR Inside Out: Large Object Heap Uncovered
The Dangers of the Large Object Heap
Analyzing Silverlight Memory Usage: Part 1 – Obtaining Measurements
Ants - Checking unmanaged memory usage
Keywords: Silverlight, memory leak, ants, vmmap, DependencyProperty