The OP is confused --- a proper garbage collector will collect cyclic structures.
The situation that he describes was a problem in versions of IE prior to ie8 because of limitations on its garbage collector --- specifically that it was unable to collect cycles if the cycle involved a DOM element. This is fixed in ie8 (and, presumably, subsequent versions), and even in ie7 the situation gets cleaned up when the user navigates away from the page (although JS that creates a lot of "throwaway" DOM elements could still get into trouble). So, most programmers really have to worry about this only if still targeting ie6.
(By the way, if you are afflicted with ie6, the jQuery 'data' APIs work around this misbehavior; other libraries often have similar tricks.)
Indeed. Also, a slightly more succinct fix for IE6/7 would be:
function foo(element, a, b) {
element.onclick = function() { /* uses a and b */ };
element = null; // avoid memory leak
}
The closure contains a reference to foo's activation object, not to its properties. As a consequence, you can clear out the element reference after the event handler has been defined and this will break the circular reference.
The above technique is also useful when creating anonymous functions for other purposes when element references happen to be lying around. Depending on how long these anonymous functions are kept alive and what you do with the elements in the meantime, this would normally lead to a memory leak (due to the dangling reference to the DOM element, rather than a circular reference as described in the article).
Indeed. Last I checked, jQuery breaks the cycle when elements are removed from the DOM by going through all their event handlers and data members and dropping them.
(Edit: I think I'm wrong here. It does indeed look like the OP is saying that the DOM node would leak after the cycle has no incoming pointers. I read it to say that other objects in the closed-over scope would be leaked as long as the reference was live).
I don't think the OP is confused. Nothing he has said is inaccurate, and you're asserting something he didn't say.
> A proper garbage collector will collect cyclic structures.
Absolutely true, so long as they are garbage. However, nothing the OP says implies that the DOM node is garbage. As I read it, the leak comes from accidentally extending the life of the enclosing scope to the life of the DOM node. That's a real leak (in the web app, not in the browser).
The problem is that the DOM is reference counted (at least in some browsers, at least back when that article was written). So, you can get memory leaks in cases where JS intersects with the DOM.
> It was confusing because JavaScript uses mark-and-sweep collection, which can handle circular references just fine.
I don't think that's mandated, you could probably implement a refcounted JS interpreter.
> The problem is that the DOM is reference counted (at least in some browsers, at least back when that article was written). So, you can get memory leaks in cases where JS intersects with the DOM.
And the main culprit was, as is usually the case, Internet Explorer.
There is a better explanation at the very end of this awesome description of javascript's closures and scope resolution: http://jibbering.com/faq/notes/closures/
This is a well-known issue that we shouldn't still be discussing in 2011. Any sane JavaScript implementation already addresses this issue, and any responsible JavaScript framework will address this issue for you for the sake of older browsers (e.g. see the jQuery source at https://github.com/jquery/jquery/blob/master/src/event.js#L1...).
To say that we all do this is more than a bit of an exaggeration.
It's definitely a stretch to say we all run into these issues, but you'd be surprised how many beginner and intermediate-level developers don't know about this issue precisely because they've been insulated from it by frameworks.
There are plenty of situations where developers can find themselves dealing with insane JavaScript implementations and irresponsible JavaScript frameworks. While the article doesn't explain exactly what the issue is and which browsers it affects (leading me to believe the author himself doesn't truly understand these memory leaks), the recommendation given is a sound one.
I don't really get the difference, in both cases the a,b will be stored somewhere no? I mean, when you return that function it references a and b as the original function would have and you've done absolutely nothing different.
The argument is that because in the first case, the closure is created in a scope where element, a, and b all exist. So element is available in the closure even if it's never used.
Since now element has a property which closes over element, he argues this is a circular reference, which will eventually become a memory leak.
It seems to me that a smart JS engine could avoid this by noticing that there's no possibility of using element within the first closure, but I'm not an expert. Also it seems like a small price to pay unless you are constantly creating and destroying such widgets; surely anything associated with an unloaded page can be GC'ed regardless of circular references?
[+] [-] rst|15 years ago|reply
The situation that he describes was a problem in versions of IE prior to ie8 because of limitations on its garbage collector --- specifically that it was unable to collect cycles if the cycle involved a DOM element. This is fixed in ie8 (and, presumably, subsequent versions), and even in ie7 the situation gets cleaned up when the user navigates away from the page (although JS that creates a lot of "throwaway" DOM elements could still get into trouble). So, most programmers really have to worry about this only if still targeting ie6.
(By the way, if you are afflicted with ie6, the jQuery 'data' APIs work around this misbehavior; other libraries often have similar tricks.)
The official scoop from Microsoft: http://msdn.microsoft.com/en-us/library/dd361842%28v=vs.85%2...
[+] [-] spjwebster|15 years ago|reply
The above technique is also useful when creating anonymous functions for other purposes when element references happen to be lying around. Depending on how long these anonymous functions are kept alive and what you do with the elements in the meantime, this would normally lead to a memory leak (due to the dangling reference to the DOM element, rather than a circular reference as described in the article).
See http://jibbering.com/faq/notes/closures/ for a seriously nerdy deep-dive into closures in JavaScript.
[+] [-] victorNicollet|15 years ago|reply
[+] [-] pbiggar|15 years ago|reply
I don't think the OP is confused. Nothing he has said is inaccurate, and you're asserting something he didn't say.
> A proper garbage collector will collect cyclic structures.
Absolutely true, so long as they are garbage. However, nothing the OP says implies that the DOM node is garbage. As I read it, the leak comes from accidentally extending the life of the enclosing scope to the life of the DOM node. That's a real leak (in the web app, not in the browser).
[+] [-] wzdd|15 years ago|reply
> create a circular reference and thus, a memory leak
It was confusing because JavaScript uses mark-and-sweep collection, which can handle circular references just fine.
A bit of googling turns up this link, which explains it:
http://www.ibm.com/developerworks/web/library/wa-memleak/
The problem is that the DOM is reference counted (at least in some browsers, at least back when that article was written). So, you can get memory leaks in cases where JS intersects with the DOM.
[+] [-] masklinn|15 years ago|reply
That's because it's wrong.
> It was confusing because JavaScript uses mark-and-sweep collection, which can handle circular references just fine.
I don't think that's mandated, you could probably implement a refcounted JS interpreter.
> The problem is that the DOM is reference counted (at least in some browsers, at least back when that article was written). So, you can get memory leaks in cases where JS intersects with the DOM.
And the main culprit was, as is usually the case, Internet Explorer.
[+] [-] stanleydrew|15 years ago|reply
[+] [-] mjijackson|15 years ago|reply
To say that we all do this is more than a bit of an exaggeration.
[+] [-] spjwebster|15 years ago|reply
There are plenty of situations where developers can find themselves dealing with insane JavaScript implementations and irresponsible JavaScript frameworks. While the article doesn't explain exactly what the issue is and which browsers it affects (leading me to believe the author himself doesn't truly understand these memory leaks), the recommendation given is a sound one.
[+] [-] wccrawford|15 years ago|reply
And with the way Chrome handles tabs (in separate processes) does it really matter, since closing the tabs releases the memory?
[+] [-] masklinn|15 years ago|reply
it does not create a leak in any modern browser. The only older browser in which it does is Internet Explorer, up to (and including) version 7.
[+] [-] philwise|15 years ago|reply
[+] [-] stanleydrew|15 years ago|reply
[+] [-] nkassis|15 years ago|reply
Can someone explain this a little better?
[+] [-] neilk|15 years ago|reply
Since now element has a property which closes over element, he argues this is a circular reference, which will eventually become a memory leak.
It seems to me that a smart JS engine could avoid this by noticing that there's no possibility of using element within the first closure, but I'm not an expert. Also it seems like a small price to pay unless you are constantly creating and destroying such widgets; surely anything associated with an unloaded page can be GC'ed regardless of circular references?
[+] [-] theallan|15 years ago|reply
[+] [-] jrockway|15 years ago|reply
[+] [-] Twisol|15 years ago|reply