August 7, 2011 2 Comments
On my first implementation (took about 3 hrs), it all worked “great”, or so I thought. After watching the graph work nicely for about 1hr ( I didn’t sit and watch if you’re wondering, I used console.log()!) chrome,firefox and IE just went poooof! IE (expectedly died a lot earlier than the others). To get an idea of what went wrong I restarted everything (killed Firefox and IE, leaving chrome with a single tab), watching my CPU and Memory usage…they did nothing but climb until the chrome process hit about 1GB, I got er, “Ooops, He’s dead Jim” page…
It was obvious I had a nasty memory leak somewhere…I started going through the code, line by line. I did it over and over and only spotted one or two things that I thought could be the cause….Sadly, I was wrong so I started Googling around.
One interesting thing I found was about closure…Understanding this (or something about it can work wonders!). Here are a list of interesting things I found that helped me nail the problem down.
- Useful tips
So what did I do to “fix” my memory leaks?
After reading the stuff above, I went over the code again,
- I set properties to null after they were used, i.e. myObject.property=null
- Used the delete keyword on the properties that were set to null, i.e delete myObject.property ( Notice I said property. According to the mozilla docs, the delete keyword doesn’t work on variables that are either function references or declared using the var keyword.)- From what I understand if myObject.property is not null then it may not be garbage collected depending on how it has been used so setting to null is your best bet if you want it gone.
- I found and removed circular references (see bullet 5 above)
- I used this.myproperty to store my live data object and other properties that were used multiple places
- I set this.myproperty to null when necessary
- Re-used this.myproperty instead of creating a new object
*In 4 minutes 40MB of additional memory was used with timeout *of 10 seconds (i.e. update charts every 10 seconds instead of every second) *That's 10mb per minute and refreshed 6 times (60 seconds/10 seconds timeout) in a minute *so 10/6=1.6MB... memory per iteration/refresh...
So in my first version of the program ~1.6MB of memory was being added each time the chart was refreshed/redrawn. That is just terrible! Even more terrifying was the fact that it just grew and grew and never levelled off!
I commented out the chart drawing code and left all the other processing code to run. After monitoring the chrome process for a few minutes, about 10, the memory usage moved from about 40MB to 42MB, when I did the same thing, prior to reading up and optimizing the memory added over about 5 minutes was in excess of 100MB and kept growing, clearly my optimization was effective.
I left the chart drawing commented and left the process running for about an hour, give or take. The maximum memory I saw was 60MB and this went back down to the 40’s in seconds!
I was happy with this now so I uncommented the graph code and it started going up again, admittedly by a lot less than before, and I suppose some increase was to be expected considering the stuff had to be drawn… After seeing the memory level off without the graph I was convinced the whole thing working together could level off at an acceptable memory level. I dug through the Google charts reference and found that, I don’t need to recreate my datatable object each time.
- Instead of recreating the DataTable, I used the remove rows method of the object to remove the first row to N, where N is the row number up to which I want to remove. So if I want to get rid of row numbers 0 to 5, I would use, mytable.removeRows(0,5)
- The second thing I did was stopped the re-creation of a graph on each update (You have no idea how expensive this was!), I made the graph object a property accessible under this.mygraph – this would only ever be created once if it is null
- Finally, I updated/refactored the code so that this.mygrapgh.chart is the only thing being invoked on each update.
The graph now works with real time data and updates every second, memory usage averages about 100MB, I’ve seen 120MB but it sometimes drop as low as 80MB and hasn’t grown any more. I would certainly like to think I’ve fixed the memory leaks, if I haven’t I’ve done pretty well at whatever’s made it stay consistently low for hours on end. The graph has so far processed over 78hrs of data and the 120MB is the highest I’ve seen.