Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript, circular references and memory leaks

Tags:

From what I recall of a not too distant past, Javascript interpreters suffered from memory leaking issues when faced with circular references.

Is it still the case in the latest browsers? (e.g. Chrome, FF 3.5 etc)

like image 211
jldupont Avatar asked Jan 04 '10 14:01

jldupont


People also ask

Can you have memory leaks in JavaScript?

The JavaScript engine allocates memory when you create objects and variables in your application, and it is smart enough to clear out the memory when you no longer need the objects. Memory leaks are caused due to flaws in your logic, and they make way for poor performance in your application.

Is JavaScript garbage collected?

Some high-level languages, such as JavaScript, utilize a form of automatic memory management known as garbage collection (GC).

How do I free up memory in JavaScript?

Hence there is no explicit way to allocate or free up memory in JavaScript. Just initializing objects allocates memory for them. When the variable goes out of scope, it is automatically garbage collected(frees up memory taken by that object.)

Does garbage collection prevent memory leaks?

Although garbage collection prevents many types of memory leaks, it doesn't prevent all of them. In automatic reference counting systems, such as Perl or Objective-C, memory is leaked whenever there are cyclical references, since the reference count is never decremented to zero.


2 Answers

The vast majority of leaks we talk about with JavaScript are specifically in IE6-7 when you make a reference loop between JavaScript objects and host objects like DOM nodes.

In IE6 this is particularly pernicious in that you don't get the memory back when you leave the page; it's gone until you quit the browser. In IE7 clearing out the page does now return the memory, but you can still have difficulty when you have a long-running application. IE8 solves most of this problem properly by turning the DOM nodes into native JavaScript objects instead of host objects. (You could still trigger the leaks in IE8 by including other non-native objects like ActiveX objects in a reference loop.)

There will certainly still be small obscure memory leaks lurking around in random places for all the browsers, especially in older versions. But there's no one way to easily categorise and avoid them like with the IE refloop issue.

like image 115
bobince Avatar answered Oct 12 '22 15:10

bobince


To add to bobince answer, I did some tests with IE8.

I tried almost all examples provided at http://www.javascriptkit.com/javatutors/closuresleak/index.shtml

No one of them is leaking memory anymore (at least not in a perceivable way), except the example that removes child nodes with events still attached to them.

This type of example I think it's better explained by Douglas Crockford in his queuetest2.

This one still leaks memory on IE8 and it's pretty easy to test by simply running the test script and looking at Windows Task Manager - Performance - PF Usage. You will see PF Usage increases by almost 1MB per loop (very fast).

But in IE8 the memory is released on page unload (like navigating to a new page or reloading the same page) and obviously also when totally closing the browser.

So in order for a final user to perceive this memory leaks on IE8 (as reduced systerm performances), he needs to stay on the same page for a long time, which in now days it can frequently happen with AJAX, but this page need also to do hundreds of childnodes removal of elements with event attached to them.

Douglas Crockford test is stressing the browser with 10000 nodes added and then removed, that's excellent for showing you the issue, but in real life I never had a page that removed more than 10 elements. INMHO usually it's faster to use display: none rather than removing an entire set of nodes, that's why I don't use removeChild that much.


For whoever might be more interested in the IE8 memory leak explained above, I did another test and it seems mem leaks do not show up at all in IE8 when using innerHTML in place of appendChild/removeChild to add/remove child elements with attached events. So apparently Douglas Crockford purge function (suggested by him to prevent memory leaks in IE) is not necessary anymore in IE8 at least when using innerHTML...

(EDITED thanks to 4esn0k comment below) ...moreover Douglas Crockford purge function does NOT work at all on IE8, in his code var a = d.attributes returns NO onclick attributes (or any other onevent attributes) that were added at runtime on IE8 (they do are returned on IE7).

Douglas Crockford says:

"The purge function should be called before removing any element, either by the removeChild method, or by setting the innerHTML property."

I provide here the code for the test:

<body>        <p>queuetest2 similar to the one provided by Douglas Crockford    at http://www.crockford.com/javascript/memory/leak.html    <br>but this one adds/removes spans using innerHTML    instead of appendChild/removeChild.</p>     <div id="test"></div>        <script>        /* ORIGINAL queuetest2 CODE FROM DOUGLAS CROCKFORD IS HERE           http://www.crockford.com/javascript/memory/queuetest2.html */        (function (limit, delay)        {           var n = 0;           var add = true;            function makeSpan(n)            {               var div = document.getElementById('test');               //adding also an inline event to stress more the browser               div.innerHTML = "<span onmouseover=\"this.style.color = '#00ff00';\">" + n + "</span>";               var s = div.getElementsByTagName('span')[0];               s.onclick = function(e)                {                   s.style.backgroundColor = 'red';                   alert(n);               };               return s;           }            function process(n)            {               if(add)                                      s = makeSpan(n);               else                  s.parentNode.innerHTML = ""; //removing span by clearing the div innerHTML               add = !add;           }            function loop()            {               if (n < limit)                {                   process(n);                   n += 1;                   setTimeout(loop, delay);               }           }            loop();       })(10000, 10);     </script> </body> 
like image 32
Marco Demaio Avatar answered Oct 12 '22 14:10

Marco Demaio