JScript Object Memory Leak 8

We all know that the memory leaks in Internet Explorer are horrible. However we’ve found ways (often making beautiful code really ugly) around these issus. My general guideline has always been to make sure that you don’t keep any reference betwen JScript objects and DOM/COM objects (and the other way around) as well as make sure that no DOM/COM object is in the scope when you use anonymous functions (since the closure will contain a reference to the DOM/COM object).

I usually say that as long as you are using only native JScript objects the garbage collector will collect these. However I’ve found a case where this is not the case. The reason for the problem is described be Eric Lippert here. I’ll show you when this problem manifests itself and then discuss ways to get around it.

var obj = {};
...
obj.foo = ...
...
delete obj.foo

That’s all. The problem here is that even though we delete the field foo JScript keeps this key occupied and adds a flag so that it looks like it has been removed to JScript code. The reason according to Eric Lippert is that COM caches the references to the field and adding another key with the same name should be still be accessible from the cached reference. (Why?).

Here is a larger code snippet that does the above a lot of times so that you can actually see that it leaks memory.

// create an object to use as a hash/dictionary/cache/lookup table
var obj = {};

// define a function to make the testing easier
function addAndRemove() {
   var key;   
   for (var i = 0; i < 10000; i++) {
      // generate fairly unique key
      key = Math.random();
      
      // add key value pair
      obj[key] = key; // the value is not relevant
      
      // and then remove the key
      delete obj[key];
   }
}

window.setInterval("addAndRemove()", 50);

This does not leak very much memory but if you have a hash table like structure that is long lived where a lot of add and removes are done to it you might run into problems. This is usually the case for caches and other lookup tables. So in these cases it makes sense to work around this JScript issue.

One way to work around this is to clone the object and let it replace the old object. This is fast if the hash is empty but O(n) otherwise where n is the number of keys currently in the hash.

var tmp = {};
for (var k in obj) {
   if (obj.hasOwnProperty(k)) {
      tmp[k] = obj[k];
   }
}
obj = tmp;

You might want to build your own class to handle this internally. When the ratio between the count of the items added to the actual items in the hash is greater than a certain value you can replace the old hash object with a new one as done above.

  • http://dean.edwards.name/ Dean Edwards

    Does that mean all the times where I changed your code to use “delete” is wrong? Doh! Me and my meddlesome hackery.

  • http://erik.eae.net Erik Arvidsson

    It means that a delete will still keep the key string and few internal flags (as well as most like the hashed value of the string). However if the GC later releases that object the memory is freed. It is only an issue if the object is keept for a long time.

    In the code above; If you recreate obj after the loop JScript does not leak any more.

  • Hakan Bilgin

    Hello,
    Another “memory leak”-phonomenon manifest itself through the use of “filter” in CSS declarations. When a filter, any, is applied to an object than the allocated memory for this isn’t released, even if the object that is attached to is deleted or the removal of the CSS-rule. Actually, just the declaration “filter:;” causes the allocation of memory and sluggish browser. Extremly annoying behaviour…

    I have mentioned this at Dave Massy’s bussy blog, hopefully this (among others) will be attended to.
    /hbi

  • http://erik.eae.net Erik Arvidsson

    I thought that setting style.filter = “” before removing the element worked?

  • Guilherme Blanco

    HAPPY BIRTHDAY Oracle!!! =)

    Peace, health, etc…

    This is a message from one of your friends that you never met.

    Cheers,

  • Patrick Corcoran

    I’m curious why you would use delete instead of assigning a null value?

  • http://erik.eae.net Erik Arvidsson

    Because delete removes the key/value pair. Assigning null keeps the key and changes the value. If you just change to null the in operator (and hasOwnAttribute method) and for in loops will still include the key/value pair you want to remove

  • John Doe

    What happens in the following code…

    var obj = {};

    obj.foo = {
    a: function() { this.a = “”; this.b = 10; }, b: function() { … } };

    var myObj = new obj.foo.a();

    delete obj.foo