How do I create a memory leak test script?

It becomes more and more apparent that creating the new addEvent() function is not for the fainthearted. I underestimated the many problems, and in the comments to my previous entry compelling arguments were raised against the winning code.

I've decided to take a few steps back and study the problem before messing about with the solution. I want to write a script that leaks memory. I'd like to judge how bad it is in practice and I'd like to see some practical code examples. Unfortunately I just can't get any function to leak memory. I'm afraid I just don't get it.

I'm trying to create practical example scripts that cause memory leaks, and similar example scripts that don't cause them. The purpose is to measure the memory leaks and to create simple pairs of Good Code/Bad Code examples for people like myself who don't quite get the problem yet.

The benchmark test pages currently in development generate 10,000 links. Then I apply the example scripts to these links, reload the page 10 times and check the Windows Task Manager or the Apple Activity Monitor to see if the browser continues to expand its memory, and if so how much extra memory it takes.

At the moment I'm using this test setup:

function init() // called onload
{
	createLinks();
	var x = document.getElementsByTagName('a');
	for (var i=0;i<x.length;i++)
	{
		// do something that causes memory leaks
	}
}

function createLinks()
{
	var x = document.getElementById('writeroot');
	var y = document.createElement('a');
	y.appendChild(document.createTextNode('A link - '));
	y.href = '#';
	for (var i=0;i<10000;i++)
		x.appendChild(y.cloneNode(true));
}

For instance, here is an example script that doesn't cause memory leaks (and I didn't expect it to, either).

To the test page. WARNING: the script is very slow, since it creates 10,000 links and then adds event handlers to all of them. You'll have to wait for at least 5 seconds before you see a page.

function init()
{
	createLinks();
	var x = document.getElementsByTagName('a');
	for (var i=0;i<x.length;i++)
	{
		x[i].onclick = function () {
			this.style.backgroundColor = '#cc0000';
		}
	}
}

Unfortunately I can't get any of my example scripts to leak memory.

Can someone please give an example of a SIMPLE function that I can just insert in my test structure and that causes memory leaks?

This is the blog of Peter-Paul Koch, web developer, consultant, and trainer. You can also follow him on Twitter or Mastodon.
Atom RSS

If you like this blog, why not donate a little bit of money to help me pay my bills?

Categories:

Comments

Comments are closed.

1 Posted by Tino Zijdel on 19 October 2005 | Permalink

Here's an example: http://therealcrisp.xs4all.nl/upload/addEvent_winnerv2.html ;)

You can use for example drip (currently available at http://www.outofhanwell.com/ieleak/ ) to check for memory leaks in IE

2 Posted by Tino Zijdel on 19 October 2005 | Permalink

Some classic examples:

//-- leak by closures (note how I reference the 'el' variable instead of using the 'this' keyword)
el = document.createElement('span');
text = document.createTextNode('whatever ');
el.appendChild('text');
el.onclick = function() { alert(el.firstChild.nodeValue); }
document.body.appendChild(el);

//-- leak by referencing another DOM element
el = document.createElement('span');
text = document.createTextNode('whatever ');
el.appendChild(text);
el.__text = text;
document.body.appendChild(el);

3 Posted by Tino Zijdel on 19 October 2005 | Permalink

Created some sample pages to illustrate:

http://therealcrisp.xs4all.nl/upload/leaktest1.html
http://therealcrisp.xs4all.nl/upload/leaktest2.html

Just open one of these pages in IE, open up your taskmanager. Check the memory-usage for IE. Refresh a couple of times and note the constant increase of memory-usage.

4 Posted by kala on 19 October 2005 | Permalink

Bujakasha!

Tino's leakage script really works. I'm only now starting to realize how serious issue this IE memoryleakage is. More than 1000K per reload.

The addEvent() script should also work with IE5, meaning it should also run with older computers. These machines are not so rich in memory thus memory leakage can really cause the whole browser, or worse yet, the whole computer crash.

5 Posted by Dean Edwards on 19 October 2005 | Permalink

You can use this custom tool to test memory leaks in IE:

http://www.outofhanwell.com/ieleak/

6 Posted by Martijn Overweg on 19 October 2005 | Permalink

It seems strange to me that a high-level language like JavaScript should burden its users with a low-level problem like memory leaking. Is this really something we (web developers) are supposed to be dealing with?

I must admit I'm not a very experienced JavaScripter, but the proposed solutions remind me of all the css hacks we're trying not to use.
Who knows, IE7 might even contain some patch that makes custom functions redundant.

7 Posted by Tino Zijdel on 19 October 2005 | Permalink

Martijn: it's not a problem with the language itself, but with the implementations. Most browsers have it reasonably under control, except IE... and yes, that means that we have to deal with it...

8 Posted by Tarquin on 20 October 2005 | Permalink

In theory, JavaScript garbage cleanup should take care of it all for you. But you could always do something like creating an infinite binary tree (make sure you use timeouts to prevent browsers aborting). Attach it to a global object so it never gets collected.

But just so we are aware, the windows task manager does not say how much memory you are actually using. It says how much the system has set aside. The program might not have actually requested all that memory, and the system might be giving it a lot more than it actually asked for. The number is a virtual number that may even be shared across libraries loaded by different apps, so it cannot really be trusted. If the number climbs, it is often windows being overly generous, and alotting more and more memory to the program, even if it is not being used.

To get a better idea, minimise the program (windows will then remove the alotted & unused memory), and the number you get there is approximately what is actually being used - often a lot less (sometimes one tenth the amount) than the number while it is maximised.

However, that is also not accurate as it does not take pagefile usage into account, but it is better than the number when the program is maximised.

9 Posted by Kelly on 20 October 2005 | Permalink

M$ has a pretty good discussion of IE's leaks here:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ietechcol/dnwebgen/ie_leak_patterns.asp

I could never get their "DOM Insertion Order Leak Model" to leak, but their closure examples leak just fine. It is possible that IE6 fixed the DOM order leak and since I can't be bothered to even look at IE5, I'll never know.

10 Posted by James Mc Parlane on 20 October 2005 | Permalink

Kelly - great link!

Rather shocked to find out that IE uses COM "reference counting" instead of the modern "mark and sweep" method which is immune to circular references.

I used to believe that the issue was simply a blind-spot in IEs garbage collector with event listeners, but from this article it seems that all circular references that include DOM elements are potentially harmful and the only antidote is effective cleanup code.

11 Posted by Laurens van den Oever on 20 October 2005 | Permalink

I just worked on a generic fix for this problem [1]. That page also contains a few example scripts.

1. http://laurens.vd.oever.nl/weblog/items2005/closures/

12 Posted by Tino Zijdel on 20 October 2005 | Permalink

In some cases it is sufficient to nullify the variable you used in your closure, like in my 1st example putting el = null; at the end of create_closure() fixes the memory-leak.
Unfortunately this cannot be used in all situations.

13 Posted by M. Schopman on 22 October 2005 | Permalink

IE in fact uses a mark and sweep garbage collector, but unfortunately it failes terribly in detecting circular references very well.