Wednesday, November 23rd, 2005

QuirksMode: Memory Leaks

Category: Editorial

<p>There is the theory of a platform, and there is the practicality of an implementation of that platform.

In our world, we need to deal with the implementations of JavaScript in the browsers we are targetting.

Quirksmode is talking about memory leaks and a thread on Dojo jumped in on one of the issues too.

Bob Ippolito of MochiKit and Dojo have some good things to say:

As Alex said, Dojo should deal with this automagically because it has unload handlers and all that fancyness when using connect. However, I think you might misunderstand what’s going on a little bit.

To the best of my understanding, closures are just ancillary.. the real reason is that you have circular references to COM objects (DOM elements), which can’t participate in JScript’s garbage collection.

The way around this is to create your event handlers such that they don’t have the DOM element in scope.. here’s an example of a circular reference and how to rewrite it without one:

// creates a circular reference
function setClick(elem) {
  elem.onclick = function () {
    // elem is visible in this scope, so its uncollectible:
    // elem - elem.onclick - parent scope (activation object) - elem
    alert("element clicked");
  }
}

// doesn't create a circular reference
function _elementClicked() {
  // elem is not visible in this scope
  alert("element clicked");
}

function setClick(elem) {
  elem.onclick = _elementClicked;
}

Closures

This lead to the discussion of when to use:

function Constructor() {
  this.method = function () {
  }
}

versus the non-closure version:

function Constructor() {};
with (Constructor.prototype) {
  method = function () {};
}

The closure form is cleaner, and once you stop working with closures, you see their power.

Bob wasn’t suggesting you stop using them:

The only time it can matter is the specific instances when they cause circular references to certain kinds of objects that don’t fully participate in the interpreter’s garbage collection scheme (most commonly DOM objects in IE).

Now and then though you will run into a memory leak and this could be a simple way to deal with it.

Do you have any tips and tricks for avoiding memory leaks in JavaScript applications?

Related Content:

5 Comments »

Comments feed

There is Function#closure [1] by Laurens van den Oever, and my Event Cache [2] script I wrote a while back which is inspired on Dojo. Another option is to set the variables with DOM references to `null` when you no longer need them, so it won’t create a circular reference.

[1]: http://laurens.vd.oever.nl/weblog/items2005/closures/
[2]: http://novemberborn.net/javascript/event-cache

Comment by Mark Wubben — November 23, 2005

Mark,

Thanks a lot for the links. Great stuff.

Cheers,

Dion

Comment by Dion Almaer — November 23, 2005

Firefox has a similar leak involving addEventListener and closures: https://bugzilla.mozilla.org/show_bug.cgi?id=241518.

What do you do if your code actually relies on closures? For example, http://www.squarefree.com/userscripts/valid-xhtml.user.js takes advantage of closures and I think it has to because of XPCNativeWrapper rules. Maybe I could work around that by creating an associative array of message divs to textareas, but that would not be fun. Furthermore, I think the entire Greasemonkey script is treated as a closure, although that might not be a problem because I don’t see any top-level variables that hold references to DOM objects beyond initialization.

Comment by Jesse Ruderman — November 23, 2005

As I said — closures are NOT the issue, circular references are. It just so happens that the way closures in JS are implemented that circular references are really really easy to create.

All you need to do is create a separate function that takes everything it needs to know (and nothing else) as parameters, and returns the closure — instead of putting the closure return inline to the function that has a reference to the DOM object.

This is what the pattern looks like in practice:
http://rafb.net/paste/results/ulZAbq36.html

Comment by Bob Ippolito — November 23, 2005

function Constructor() {
this.method = function () {
}
}

I see no closure or circular reference there.

Comment by Gonzalo — November 27, 2005

Leave a comment

You must be logged in to post a comment.