I spent a great part of the last 24 hours trying to chase down a couple of memory leaks in a javascript project I’m working on. After much hair-pulling, a couple of observations (and no more memory leaks) have resulted:
1. jQuery’s element creation guides leave a lot to be desired. On their official site, you can read the following example under “Creating New Elements”:
$('<p id="test">My <em>new</em> text</p>').appendTo('body');
Later, they discuss creating elements as follows:
$('<input />', {
type: 'text',
name: 'test'
}).appendTo("body");
I’m only targeting WebKit and Mozilla browsers for this project (I have the luxury of doing so), so I’m not concerned with IE quirks. What does concern me is that creating elements as in the first example causes memory leaks if you do it a few million times (for example, updating some page element to reflect incoming realtime data). If you put a string of complex HTML into $()
, it seems like jQuery is doing something to cache the parsed fragment that does NOT get erased even if you call .html(”), .empty(), or .remove() on its parent. The element is merely removed from rendering. Elements created the second way will be fully removed from memory instead of placed in some #document-fragment or $.fragments cache (this stackoverflow discussion seems to be very similar to the problem I experienced). So even though the second syntax is far less wieldy for making complex little HTML updates, it doesn’t leak.
2. jQuery Sparklines is a nice little jQuery plugin to allow you to make sparklines out of some data on your page. Data visualization is fun and everyone likes it, but I was trying to troubleshoot the memory leak above and even after I fixed that, I was still observing memory leaks related to Sparklines. Sparklines is sort of indirectly to blame. It keeps a “pending[]” array that links to your page elements and stores any sparklines that aren’t visible until you call $.sparkline_display_visible()
to render anything in the pending[] array. This is nice for static pages, but it can have the undesirable side effect of stacking up millions up sparklines (in itself a sort of memory leak) on dynamic pages by the time someone gets around to clicking the tab, even if those sparklines were attached to elements that have been removed from the DOM via .remove(), .empty(), or html(”) — the latter cases, of course, are effectively memory leaks since references are hanging around in that “pending[]” array. The easy fix is just to never request a sparkline for a hidden element in the first place, but it still feels clunky to me. It would have saved me some time if this implication of Sparklines tracking requests for hidden elements was explicitly documented.
(My use-case is replacing cells in a table based on real-time updates via WebSockets; some of these updates are used to generate sparklines that go stale on the next update for a given category, so if they haven’t been observed, they should be discarded.)
Yay for memory leak troubleshooting – pretty much my least favorite part of coding ever.:)
Emailed.:) Thanks for the response!
Interesting timing – I actually just fixed a memory leak around pending hidden sparklines the other day for 2.0 of the plugin, which hopefully I’ll release this weekend.
That issue occurs when there are repeated calls to render a sparkline on a hidden element, but won’t help with sparklines that have subsequently been removed from the DOM – I may try to address that prior to releasing 2.0 – Send me an email if you’d like to help test!