IE8 compliant weakmap for DOM node references - javascript

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?

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.

Related

Is it necessary to nullify primitive values for grabage collection?

If I have the following code:
function MyClass() {
this.data = {
// lots of data
};
}
var myClassInstace = new MyClass();
var myobj = {
num:123,
str:"hello",
theClass:myClassInstance
};
I know it's absolutely necessary to do:
myobj.theClass = null;
To free up myClassInstance and its data property for GC. However, what should I do with myobj.num and myobj.str? Do I have to give them a value of null too? Does the fact that they're primitive change anything regarding GC?
The JavaScript runtime that implements garbage collection will be able to collect items as soon as values are no longer reachable from code. This is true for object references as well as primitives. The details of the exact moment the item is collected varies by implementation, but it is not even necessary to set your object references to null (as you state) unless you need the object cleaned up sooner than the natural termination of the current function.
This all ties into the fundamental concept of "scope" and the Scope Chain. When an item is no longer in any other objects scope chain it can be collected. Understanding this clearly will answer this question and also help to understand closures, which are scenarios where items stay in memory longer than you might have expected.
There are a lot of "it depends here", ranging from what your code is doing to what browser you're running in. However, if your object is JIT compiled to not use a map for its attributes, then the number should be an 8 byte double stored inline inside the object. Nulling it will do nothing.
The string and the myclass instance will be a pointer to memory allocated outside the object (since a string can be arbitarily many bytes, it can't be stored inside the object. A compiler could conceivably store one instance of the string in memory and never free it, however). Nulling them can allow the garbage collector to free them before the main object goes out of scope.
However, the real question is why you're worried about this. Unless you have profiled your code and identified garbage collection or memory leaks as a problem, you should not be trying to optimize GC behavior. In particular, unless your myobj object is itself going to be live for a long time, you should not worry about nulling fields. The GC will collect it when it goes out of scope.
setting to undefined (not null) will work however delete is better example delete myobj.theClass
Just to avoid misunderstanding I will say that there is no way to really delete an object from memory in JavaScript. you delete it's references or set them to undefined so that the GC can do it's work and really delete.

Do javascript closures with a this/self reference cause memory leaks?

From what I understand about memory leaks, referencing an out-of-scope var within a closure will cause a memory leak.
But it is also a common practice to create a "that" var in order to preserve the "this" reference and use it within a closure, especially for events.
So, what's the deal with doing stuff like this:
SomeObject.prototype.createImage = function(){
var that = this,
someImage = new Image();
someImage.src = 'someImage.png';
someImage.onload = function(){
that.callbackImage(this);
}
};
Wouldn't that add a little leakage to a project?
Yes, yes it does cause memory leaks, at least in some browsers (guess which). This is one of the more compelling reasons to put your trust in one of the various frameworks available and to set up all your event handlers via its mechanisms instead of directly adding "DOM 0" event handlers like that.
Internet Explorer (at least prior to 9, and possibly including 9) has two memory allocation mechanisms (at least) internally: one for the DOM, and one for JavaScript (well JScript). They don't understand each other. Thus even if a DOM node is freed up, any closure memory as in your example will not be freed.
edit — Oh, and the reason I mention frameworks is that they generally include code to try and mitigate this problem. The avoidance of attaching anything to DOM node properties is one of the safest approaches. All browsers worth worrying about (including ancient IE versions) have alternative ways of attaching event handlers to DOM nodes, for example.
Shot in the dark here, but I reckon that:
someImage.onload = function(){
that.callbackImage(this);
someImage.onload = null
}
would clean up the "memory leak" left by that
There was a time with IE that circular references involving DOM elements (which were typically formed by closures when assigning a function to a listener) caused memory leaks, e.g.
function foo() {
var someEl = document.getElementById('...');
someEl.onclick = function() {...};
}
However, I think they are fixed or patched sufficiently that unless testing shows otherwise, they can be ignored. There are also a number of ways to avoid such closures, so even if they are an issue, they can be worked around (e.g. don't create circular references involving DOM elements).
Edit
Using libraries or any other method of attaching listeners can still create circular references and memory leaks, e.g. in IE:
function foo() {
var el = document.getElementById('d0');
// Create circular reference through closure to el
el.attachEvent('onclick', function(){bar(el);});
}
function bar(o) {
alert(o == window.event.srcElement); // true
}
window.onload = foo;
The above uses attachEvent to add a listener (which pretty much all frameworks will use for IE < 9, including jQuery) yet still creates a circular reference involving a DOM element and so will leak in certain versions of IE. So just using a library will not fix the issue, you need to understand the causes and avoid them.

Javascript object memory management when using delete on property

I'm currently writing a node.js/socket.io application but the question is general to javascript.
I have an associative array that store a color for each client connection. Consider the following:
var clientColors = new Array();
//This execute each new connection
socket.on('connection', function(client){
clientColors[client.sessionId] = "red";
//This execute each time a client disconnect
client.on('disconnect', function () {
delete clientColors[client.sessionId];
});
});
If I use the delete statement, I fear that it will make a memory leak as the property named after client.sessionId value(associative arrays are objects) won't be deleted, its reference to its value will be gonne but the property will still exist in the object.
Am I right?
delete clientColors[client.sessionId];
This will remove the reference to the object on object clientColors. The v8 garbage collector will pick up any objects with zero references for garbage collection.
Now if you asked whether this created a memory leak in IE4 then that's a different question. v8 on the other hand can handle this easily.
Seeing as you deleted the only reference the property will also be gone. Also note that objects are not "associative arrays" in javascript since ordering is implementation specific and not garantueed by the ES specification.
Since clientColors[client.sessionId] is a primitive value (a string) in this case, it will be cleared immediately.
Let's consider the more complicated case of foo[bar] = o with o being a non-primitive (some object). It's important to understand that o is not stored "inside" foo, but somewhere in an arbitrary memory location. foo merely holds a pointer to that location. When you call delete foo[bar], that pointer is cleared, and it's up to the garbage collector to free the memory taken by o.
BTW: You shouldn't use Array when you want an associative array. The latter is called Object in Javascript and is usually instantiated using the short-hand quasi-literal {}
If you are using the V8 engine or nodejs/io, it may not lead to a leak but it is always advisable to prevent leaks.
Just delete it
delete clientColors[client.sessionId];
Or set it to null
clientColors[client.sessionId] = null;
Which will also cascade to any prototypically inherited objects.
This way there is almost no probability of starting a leak.

Using jQuery and Memory Leaks

I have been using jQuery for over a couple of months and read up on Javascript memory leaks for a few days.
I have two questions regarding memory leaks and jQuery:
When I bind (using .bind(...)) do I have to unbind them (.unbind()) if I leave the page/refresh to avoid memory leaks or does jQuery remove them for me?
Concerning closures, I read that they can lead to memory leaks if used incorrectly. If I do something such as:
function doStuff( objects ){ //objects is a jQuery object that holds an array of DOM objects
var textColor = "red";
objects.each(function(){
$(this).css("color", textColor );
});
}
doStuff( $( "*" ) );
I know that the above code is stupid (better/simpler r ways of doing this) but I want to know if this causes circular references/closure problems with .each and if it would cause a memory leak. If it does cause a memory leak, how would I rewrite it (usually similar method) to avoid a memory leak?
Thanks in advance.
Edit: I have another case similar to question 2 (which I guess makes this part 3).
If have something like this:
function doStuff( objects ){ //iframe objects
var textColor = "red";
function innerFunction()
{
$(this).contents().find('a').css("color", textColor );
}
objects.each(function(){
//I can tell if all 3 are running then we
//have 3 of the same events on each object,
//this is just so see which method works/preferred
//Case 1
$(this).load(innerFunction);
//Case 2
$(this).load(function(){
$(this).contents().find('a').css("color", textColor );
});
//Case 3
$(this).load(function(){
innerFunction();
});
});
}
doStuff( $( "iframe" ) );
There are 3 cases above and I would like to know which method (or all) would produce a memory leak. Also I would like to know which is the preferred method (usually I use case 2) or better practice (or if these are not good what would be better?).
Thanks again!
1) No. The browser clears everything between page loads.
2) In its current form there will be no memory leak, since jquery's .each() function doesn't bind anything, so once its execution is finished, the anonymous function it was passed is no longer reachable, and therefore the environment it closed (i.e. the closure as a whole) is also not reachable. So the garbage collection engine can clean everything up - including the reference to objects.
However, if instead of .each() you had something harmless, like $('div:eq(0)').bind() (I'm trying to emphasise that it needn't be a reference to the large objects variable, enough that it is even a single unrelated element), then since the anonymous function sent to .bind() closes the objects variable, it will remain reachable, and therefore not garbage collected, allowing memory leaks.
A simple way to avoid this problem, is to objects = null; at the end of the executing function.
I should note that I'm not familiar with the JS garbage collection engines, so it is possible that there are reasonably intelligent optimisations. For example it can be checked whether or not the anonymous function tries to access any variables closed in with it, and if not, it might pass them to the garbage collector, which would solve the problem.
For further reading, look for references to javascript's memory model, specifically the environment model and static binding.
There are some subtle leak patterns that you may not even recognize. Check out a question I asked some time ago about a similar issue
jQuery 1.5 Memory leak in IE8
If you create a closure around an object which references the dom node, a leaking reference loop is made, and unfortunately, it can not be corrected by simply unbinding. You will have to set the reference to the DOM node in the object to null.
In regards to 1., no, you definitely do not have to .unbind() when leaving a page. I am not entirely sure about two.
About 1, sometimes you have to free your resources especially when you have circular references and using Internet Explorer (because of some bugs, because in theory you shouldn't have to do that) ;) Google maps had a function in their v2 to prevent that that we had to call on document.onunload (GUnload).
About 2, you don't have circular references. It consumes a lot of memory since the this must have its own context of execution to access textColor among others..
A circular reference is achieved when an object references to itself or a closure calls itself. Maybe there's other situations..
Hope this helps

How to remove DOM elements without memory leaks?

My JavaSript code builds a list of LI elements. When I update the list, memory usage grows and never goes down. I tested in sIEve and it shows that the browser keeps all elements that were supposed to be deleted by $.remove() or $.empty jQuery commands.
What should I do to remove DOM nodes without the memory leak?
See my other question for the specific code.
The DOM preserves all DOM nodes, even if they have been removed from the DOM tree itself, the only way to remove these nodes is to do a page refresh (if you put the list into an iframe the refresh won't be as noticable)
Otherwise, you could wait for the problem to get bad enough that the browsers garbage collector is forced into action (talking hundreds of megabytes of unused nodes here)
Best practice is to reuse nodes.
EDIT: Try this:
var garbageBin;
window.onload = function ()
{
if (typeof(garbageBin) === 'undefined')
{
//Here we are creating a 'garbage bin' object to temporarily
//store elements that are to be discarded
garbageBin = document.createElement('div');
garbageBin.style.display = 'none'; //Make sure it is not displayed
document.body.appendChild(garbageBin);
}
function discardElement(element)
{
//The way this works is due to the phenomenon whereby child nodes
//of an object with it's innerHTML emptied are removed from memory
//Move the element to the garbage bin element
garbageBin.appendChild(element);
//Empty the garbage bin
garbageBin.innerHTML = "";
}
}
To use it in your context, you would do it like this:
discardElement(this);
This is more of an FYI than an actual answer, but it is also quite interesting.
From the W3C DOM core specification (http://www.w3.org/TR/DOM-Level-2-Core/core.html):
The Core DOM APIs are designed to be compatible with a wide range of languages, including both general-user scripting languages and the more challenging languages used mostly by professional programmers. Thus, the DOM APIs need to operate across a variety of memory management philosophies, from language bindings that do not expose memory management to the user at all, through those (notably Java) that provide explicit constructors but provide an automatic garbage collection mechanism to automatically reclaim unused memory, to those (especially C/C++) that generally require the programmer to explicitly allocate object memory, track where it is used, and explicitly free it for re-use. To ensure a consistent API across these platforms, the DOM does not address memory management issues at all, but instead leaves these for the implementation. Neither of the explicit language bindings defined by the DOM API (for ECMAScript and Java) require any memory management methods, but DOM bindings for other languages (especially C or C++) may require such support. These extensions will be the responsibility of those adapting the DOM API to a specific language, not the DOM Working Group.
In other words: memory management is left to the implementation of the DOM specification in various languages. You would have to look into the documentation of the DOM implementation in javascript to find out any method to remove a DOM object from memory, which is not a hack. (There is however very little information on the MDC site on that topic.)
As a note on jQuery#remove and jQuery#empty: from what I can tell neither of these methods does anything other than removing Objects from DOM nodes or removing DOM nodes from the document. They only remove That of course does not mean that there is no memory allocated to these objects (even though they aren't in the document anymore).
Edit: The above passage was superfluous since obviously jQuery cannot do wonders and work around the DOM implementation of the used browser.
Have you removed any event listeners? That can cause memory leaks.
The code below does not leak on my IE7 and other browsers:
<html>
<head></head>
<body>
add
<ul></ul>
<script>
function addRemove(a) {
var ul = document.getElementsByTagName('UL')[0],
li, i = 20000;
if (a.innerHTML === 'add') {
while (i--) {
li = document.createElement('LI');
ul.appendChild(li);
li.innerHTML = i;
li.onclick = function() {
alert(this.innerHTML);
};
}
a.innerHTML = 'remove';
} else {
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
a.innerHTML = 'add';
}
}
</script>
</body>
</html>
May be you can try to spot some differences with your code.
I know that IE leaks far less when you insert first the node in the DOM before doing anything to it, eg: attaching events to it or filling its innerHTML property.
If you have to "post-fix" leakage, and must do so without rewriting all your code to take closures, circular references etc in account, use Douglas Crockfords Purge-method prior to delete:
https://crockford.com/javascript/memory/leak.html
Or use this closure-fix workaround:
Leak Free Javascript Closures

Categories