Question

I want to have literally a Dictionary<Node, Object>

This is basically an ES6 WeakMap but I need to work with IE8.

The main feature I want is

  • minimize memory leaks
  • O(1) lookup on Object given Node.

My implementation:

var uuid = 0,
    domShimString = "__domShim__";

var dataManager = {
    _stores: {},
    getStore: function _getStore(el) {
        var id = el[domShimString];
        if (id === undefined) {
            return this._createStore(el);
        }
        return this._stores[domShimString + id];
    },
    _createStore: function _createStore(el) {
        var store = {};
        this._stores[domShimString + uuid] = store;
        el[domShimString] = uuid;
        uuid++;
        return store;
    }
};

My implementation is O(1) but has memory leaks.

What's the correct way to implement this to minimize memory leaks?

Was it helpful?

Solution

In an article I wrote recently, ES 6 - A quick look at weak maps, I explained how jQuery is able to make data() leak free. It basically generates an expando property name, jQuery.expando. When you attach data to an element, the data is pushed to an internal cache array, and the element is given the expando property with a value of the index of the data in the cache. Something similar to this:

element[jQuery.expando] = elementId;

The way to prevent circular references is to not attach objects directly to elements as expandos. If a reference to the element remains in code, then that element cannot be garbage collected even if it is removed from the DOM. However, preventing circular references doesn't plug the leak entirely - there's still data left in the array if the element is removed from the DOM and garbage collected. So jQuery clears the array on page unload, as well as removing data from the array if elements are removed from the DOM using its own methods like remove(). It keeps the data alive for detach().

The reason jQuery does this, is because there is no weak map equivalent, it's kind of shimmable in ES 5 but not in ES 3. As explained in my article, WeakMap is made for exactly this kind of situation, but the only available implementation is in Firefox 6 and above and, with the spec not being finalized, even that shouldn't be used in production environments.

Another thing to take from my article is that certain elements will not allow you to attach expando properties — <object> and <embed> are the two culprits named and shamed in the jQuery source code. For these element's, you're pretty much screwed and jQuery just will not let you use data on them.


Basic circular reference memory leaks occur in reference counted implementations when two object's properties hold direct references to each other. So DOMObject holds a reference to JSObject and vice versa. Assuming there are no other references to either object, they'd both have a permanent reference count of 1 and the GC would not mark them for collection.

Older browsers (IE6) wouldn't break these circular references, even on page unload, whilst newer browsers are able to break many of these circular references by recognizing the patterns that cause them. jQuery.cache and similar patterns partially void memory leaks because DOMObject never holds a reference to JSObject so, even when JSObject holds a reference to DOMObject, the GC can still mark JSObject for collection when there are no more references to it. Once the GC has collected JSObject, the reference count for DOMObject will be reduced, freeing that up for collection also.

Although IE 8+ and other reference counting browsers may be able to break many circular reference patterns (around 400 were fixed for IE 8), the likelihood of leaks is only reduced. For instance, I've seen a huge leak in one of my own apps in IE 8, when working with script elements and JSONP. The best solution is to plan for the worst and, without WeakMap(), the best you can do is use the jQuery data pattern. Sure, you might be risking having orphaned objects, but this is the lesser of two evils.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top