Drip - Perhaps the most useful tool for debugging scripts in MSIE

If you write client-side scripts of any kind, go get Drip right now. You won’t regret it.

Given the right kind of conditions, MSIE can leak (and hog) memory like a bucket without a bottom. Plugging these holes is a royal PITA, exacerbated by the fact that they can be very tricky to find in a lispy lambda language like Javascript. What’s worse, most of the literature on the subject is incomplete, misleading, or flat-out wrong, like the word from M$ that closures are the culprit.

Here’s how it works:

  1. A Javascript object refers to a DOM node.
  2. A DOM node has a reference to a Javascript object.
  3. These references are not explicitly broken before the window unloads.

When the page unloads, IE is supposed to collect all the garbage, and free the memory up for other programs to use (or for itself to use on a new page.) This cleanup never happens, because it thinks that the object is still in use—A refers to B, so we can’t destroy B, and B refers to A, so we can’t destroy A. Break the chain, and all is fixed.

Closures don’t cause this to happen, but they do make it harder to detect by reading through the code. If you’re actually using Javascript in the way that it is designed, with closures and setTimeouts and so on, well, god help you.

Drip shows you which DOM nodes are not released when the page unloads. It’s not a magic bullet–you still have to go through your code and figure out why these DOM nodes aren’t being released. But at least you can see if it’s happening, and to which elements.

Drip isn’t only good for memory leaks, that is, situations where the memory is never freed onunload. MSIE also hogs memory in some scenarios. This is called memory “creep”. We recently uncovered a bug where (never on his machine, but always on mine) MSIE grabbed 36,864 bytes each time a certain function fired. These bytes were never returned. I made the mistake of leaving IE on the page over the long July 4th weekend, and came back to find it hogging up over half of my computer’s memory. Since it wasn’t a leak, there was no way to determine exactly which nodes were the problem.

A little research turned up that IE allocates 9216 bytes for a DOM node reference. 9216*4=36,864. Here’s what was happening, in a nutshell:
(names have been changed to protect the innocent, and to prevent any shadow of violating feduciary duty to Yahoo.)

myObj=function() {
  // private and privileged stuff.
  // other public member functions and variables...
  blah:function(i) {
    var newNode=this.nodes[i];
    var newOtherNode=this.otherNodes[i];
    var oldNode=this.currentNode;
    var oldOtherNode=this.currentOtherNode;
    function doSomething() {
    function doSomethingElse() {
    this.timeout = setTimeout(doSomething,1000);
    this.timeout2 = setTimeout(doSomethingElse,1000);

So, the newNode, newOtherNode, oldNode, and oldOtherNode were never getting released on my machine, but on his they were. Odd. Couldn’t just set them to null at the end of the function, either, because they’re used in the scope of that function but after the function ends! So, we set them to null in the enclosed functions, and there was no more memory creep.

A word of warning: Drip will tell you that your beautiful code eats up memory, and force you to trudge through trace after trace to find the reference that you forgot about. It might hurt your feelings (but probably not as badly as jsLint.) Someday, when IE7 has become the new IE6, this may all be a relic of an earlier time, like document.layers or the SPACER tag.

Leave a Reply

Comments are moderated like crazy using a variety of plugins. There is a very high likelihood that your comment won't show up right away, especially if you have never commented here before, but it was not deleted.

Please be patient, and do not post your comment more than once. It will show up once it is approved.

You must be logged in to post a comment.