Tuesday, July 28th, 2009

Dynamic script generation and memory leaks

Category: JSON, Performance

<p>An interesting piece by Neil Fraser shows that using JSON-P with generated script nodes can be quite a memory leak. Normally you’d add information returned from an API in JSON-P with a generated script node:

javascript
< view plain text >
  1. script = document.createElement('script');
  2.   script.src = 'http://example.com/cgi-bin/jsonp?q=What+is+the+meaning+of+life%3F';
  3.   script.id = 'JSONP';
  4.   script.type = 'text/javascript';
  5.   script.charset = 'utf-8';
  6.   // Add the script to the head, upon which it will load and execute.
  7.   var head = document.getElementsByTagName('head')[0];
  8.   head.appendChild(script)

The issue there is that you clog up the head of the document with lots of script nodes, which is why most libraries (like the YUI get() utility) will add an ID to the script element and remove the node after successfully retrieving the JSON data:

javascript
< view plain text >
  1. var script = document.getElementById('JSONP');
  2. script.parentNode.removeChild(script);

The issue with this is that browsers do remove the node but fail to do a clean garbage collection of the JavaScript at the same time. This means to cleanly remove the script and its content, you need to also delete the properties of the script by hand:

javascript
< view plain text >
  1. // Remove any old script tags.
  2.   var script;
  3.   while (script = document.getElementById('JSONP')) {
  4.     script.parentNode.removeChild(script);
  5.     // Browsers won't garbage collect this object.
  6.     // So castrate it to avoid a major memory leak.
  7.     for (var prop in script) {
  8.       delete script[prop];
  9.     }
  10.   }

Read the whole post here

Related Content:

Posted by Chris Heilmann at 3:50 pm
15 Comments

+++--
3.8 rating from 26 votes

15 Comments »

Comments feed TrackBack URI

Interessting stuff. I think one of the good things with all these memory leaks on IE is that less and less people will use it since it will clog up with complex applications written by users not testing for memory leaks. So in a sense in a few years the old leaky IE versions might be rendered useless even if people do cross browser logic.

Comment by Spocke — July 28, 2009

I don’t understand why this is unique to JSONP. Shouldn’t any DOM node you delete suffer the same leaks?

Comment by skippyK — July 28, 2009

@Spocke, I don’t know that people think that way. You might as well imagine that people will soon stop having dogs because dogs poop.

Comment by Nosredna — July 28, 2009

@WebReflection, clearAttributes doesn’t work for every DOM node. Only for elements which has that method.

Comment by steida — July 29, 2009

steida, isn’t this post related to a script node? Have you read my entry? Quick summary for lazy guys:

function destroy(node){
// WebReflection IE leak attemp!
if(node.attributes)
node.clearAttributes(); // Hedger suggestion
while(node.lastChild)
destroy(node.lastChild);
if(node.parentNode)
node.parentNode.removeChild(node);
};

Regards

Comment by WebReflection — July 29, 2009

P.S. before some other comment, this is for IE only which means other browsers do not need it. I am updating the script in my blog to make things more simple for everybody ….

Comment by WebReflection — July 29, 2009

I just corrected your statement suggested for every DOM node which was wrong and still is :).
btw, it’s my opinion, but I don’t like name “destroy”. In JS you can never ensure that element will be really destroyed. There is no such thing like destructor in JS. “Purge” would be a better name.

Comment by steida — July 29, 2009

call it purge then, destroy was from Ext JS convention and nothing else. I hope you got the point that we do not need manual for loops to remove properties in IE or a delete which could cause an exception. In few words, this post is suggesting a bad practice, without providing a valid solution, imho.

Comment by WebReflection — July 29, 2009

Would a better option not be to just re-use the nodes? If you’re injecting via JSONP, then you can easily wrap your callback in a bit of extra code to manage nodes that have returned their payload and can therefore be reused.

Comment by kissmyawesome — July 29, 2009

Why are u providing solutions to an issue that is not ours?
This is the very reason why we still have IE around and need to use so many workarounds. You are suggesting to add more and let MS continue piss us off with their crappy software.
Then, you proceed to whine how users are stupid for using IE, but in reality stupid are we for ever creating workarounds for this piece of guano.

Comment by paziek — July 29, 2009

Another way to fix the IE issue with removeChild() without looping through attributes from way back in 2007 http://blogs.telerik.com/toddanglin/posts/07-03-26/the_ajax_papers_part_ii.aspx.
.
jQuery thread exploring the use of outerHTML instead of innerHTML
http://groups.google.com/group/jquery-dev/browse_thread/thread/4a99f6e9b2e33057/45ce657a48afd43a?#45ce657a48afd43a

Comment by jdalton — July 29, 2009

Two important points:
1) The article states

every one of today’s browsers will fail to garbage collect the offending node

so all the discussion about IE specific solutions are missing the mark
2) The article provides no proof, only the assertion. The claim that every attribute must be removed from the script nodes suggests to me that not enough debugging was done. Surely if his claim is legit, only one or a handful of attributes should be removed. Without visibility into the system that had the leak, we’re left to believe him or (dis)prove his assertion.

At best, this article should be a suggestion/warning that some investigation (and detailed documentation) needs to be put into the real effects and side effects of using generated script nodes. Discovering new dhtml issues is great and helpful to web devs everywhere, but let’s be sure of the issue.

Comment by lsn — July 29, 2009

while (script = document.getElementById(‘JSONP’))
Please don’t promote such thing. id’s should be unique in a document. Assigning same “JSONP” id to every SCRIPT element is definitely not how it’s “normally done”.

Comment by kangax — July 29, 2009

@lsn I don’t believe discussing fixes for an issue that has been proven, in at least IE, is `missing the mark`. I haven’t seen proof of it effecting other browsers but using the dummy element + innerHTML solution won’t throw compat issues with other browsers. I may assume by your skepticism that a YUI patch, even to solve the IE issue, will not be coming any time soon :(

Comment by jdalton — July 29, 2009

I’m the Neil Fraser in question. Let me address a few of your questions.

@Spocke and others, this is not specific to IE. All browsers (IE, Firefox, Safari, Opera, Chrome) leak memory with this issue. Deleting the properties on the script tag eliminates this leak in all browsers except IE, as IE doesn’t let you delete native properties from DOM nodes. However in IE (but not in any other browser) one can reuse the script tag simply by resetting the SRC attribute. This important detail is mentioned in my article, but not in Ajaxian’s summary.

@skippyK, This only affect script tags since script tags are unique in having footprints in both the DOM world and the JavaScript world. The garbage collectors for each world don’t work together, so they are unable to delete this node. Normal nodes would (should) be cleaned up properly by the DOM’s garbage collector.

@WebReflection, script.clearAttributes() only works in IE, and IE is the only browser which doesn’t need this attribute pruning since the script tag can be reused.

@kissmyawesome, Other than in IE, I’m not aware of any technique which alloows one to refire a JSONP script tag.

@lsn, 100% of the measurable leak was from the src attribute which in my case was very long. The solution to delete all parameters and not just src was simply to be thorough. Measurements were done under very controlled environments over eight hour periods with data points at half hour intervals. The ‘before’ graphs on all browsers showed linear growth, the ‘after’ graphs on all browsers showed a flat line.

@kangax, there would normally only be one ‘JSONP’ script tag, but in the off chance that there were more than one, this while loop would purge all of them. The loop is for safety, not for design.

Comment by NeilFraser — August 3, 2009

Leave a comment

You must be logged in to post a comment.