Memory Leaks, createElement, and Form Controls

D

dhtml

(originally mis-posted on m.p.s.jscript...)

I've just closed all windows in Firefox and its using 244MB of memory.

I have no idea why. I had GMail open, a page from unicode, the CLJ FAQ.

I've noticed that createElement leaks. It's obvious with form controls
because the form keeps the control name as a property.

Example:

<!doctype html>
<body>
<form><input name="foo"/></form>

<script>
document.forms[0].foo;
document.forms[0].innerHTML = "";
document.write(document.forms[0].foo);
</script>
</body>

Will output:
[object HTMLInputElement]

(or similar implementation-dependent string).

If a node is added to the document, memory is allocated for that node.
When that node is removed, the memory usage goes back down, but not to
where it was before.

So FORM controls leak memory. This came up here:
http://groups.google.com/group/comp.lang.javascript/browse_frm/thread/db8447c9ef6b0710?tvc=1

So what happens if I create, say 100 divs, then remove them?

Using Firefox 3.0.1
Well, I did that. I restarted Firefox. and noted the memory usage at "53mb".

I filled in "100" for the text input and clicked "periodicCreate()".

It reached 100 after a less than a minute. I checked the memory usage
again and it was 89.38mb.

I clicked "destroy" and waited a few minutes, watching the memory
increase and Firefox became unresponsive and CPU spiked to 100%.

"destroy" completed several minutes later, and I noted the memory usage
at 85.00mb.

A minute later, real memory remains at around 85mb. I reloaded the page.
85mb. I navigated to google.com. Still at 85mb.

Here is my test page:

<!DOCTYPE html>
<html lang="en">
<head>
<title>div Memory Leak</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<style type="text/css">
input { display: none; }
</style>
</head>
<body>
<h1>Adding DIV to DIV</h1>
<button onclick="create()">create()</button>
<button onclick="destroy()">destroy()</button>
<button onclick="periodicCreate()">periodicCreate()</button>
<input type="text" value="20" style="display:block" id='per'>
<pre id="mon">
Check memory consumption before starting.
</pre>
<pre id="mon2">
-
</pre>
<div id='cont'><!-- if the form element tag is changed to "div",
the leak does not occur --></div>
<script type='text/javascript'>
var mon = document.getElementById('mon'),
keyMon = document.getElementById('mon2').firstChild;
var p = document.getElementById('cont');
keys = [];
function create() {
var n = "n" + +new Date;
keys.push(n);
setTimeout(addInputs, 10);
}

function addInputs() {
var inp = document.createElement('div'),
c,
n = keys[keys.length-1];
for(var i = 0; i < 1000; i++) {
c = inp.cloneNode(false);
// add a title property,
// to increase memory.
c.title = "T" + i;
p.appendChild(c);
}
keyMon.data += (keys.length) + " name prefix: " + n + " please
wait.\r\n";
addInputs.done();
}
addInputs.done = function(){};

function periodicCreate() {
if(periodicCreate.i == +document.getElementById('per').value)
return;
create();
addInputs.done = function() {
periodicCreate.i++;
periodicCreate();
};
}
periodicCreate.i = 0;

function destroy() {
mon.innerHTML = "setting innerHTML = ''. Please wait...";
p.innerHTML = "";
mon.innerHTML = "done. Check memory consumption again.";
}
</script>

</body>
</html>
===========================================

So, Firefox leaks memory with createElement/appendChild.
 
T

Thomas 'PointedEars' Lahn

dhtml said:
I've just closed all windows in Firefox and its using 244MB of memory.

Wait till the FIREFOX.EXE process was terminated (as initiated by closing
all of its windows), then it will use 0M of memory. Besides, which memory
exactly?
[...]
I've noticed that createElement leaks. It's obvious with form controls
because the form keeps the control name as a property.

You have noticed nothing of the sort.
Example:

<!doctype html>

<body>
<form><input name="foo"/></form>

<script>

That is not even remotely Valid, and you are complaining?
document.forms[0].foo;

This does not do anything except reference resolution.
document.forms[0].innerHTML = "";

So you are trying to create an empty `form' element, which is not Valid.

document.write(document.forms[0].foo);
</script>
</body>

Will output:
[object HTMLInputElement]

Works as designed. Good Thing.
(or similar implementation-dependent string).

So you are not even complaining only about Gecko-based UAs in the first
place? Because there is only one Gecko DOM implementation.
[...]
Here is my test page:

<!DOCTYPE html>

Not Valid again.
[...]
So, Firefox leaks memory with createElement/appendChild.

That is an incorrect assumption. Apparently you have neither understood
HTML, the DOM, or Windows memory management nor how garbage collection
works: an object that is no longer being referred to is eventually marked
for garbage collection that may or may not come later. It would be very
inefficient would garbage collection take place immediately after an object
would no longer being referred to.


PointedEars
 
D

dhtml

Stanimir said:
The later is well-formed XML (I guess SGML, too) DOCTYPE declaration.
The former appears to be valid HTML 5
<http://www.w3.org/html/wg/html5/#the-doctype>.

That doctype triggers standards mode. It won't pass the validator.

If desired, a full URI-doctype can be added to achieve the same effect.

Lets try to not focus on the extraneous noise and keep this focused and
on-topic.
 
D

dhtml

Thomas said:
Wait till the FIREFOX.EXE process was terminated (as initiated by closing
all of its windows), then it will use 0M of memory. Besides, which memory
exactly?

Closing all of the windows does not result in 0M of memory being used
(real memory).

[...]
I've noticed that createElement leaks. It's obvious with form controls
because the form keeps the control name as a property.

You have noticed nothing of the sort.

The example was shortened for brevity. Validating the html does not
change the result.

Example:

<!doctype html>

<body>
<form><input name="foo"/></form>

<script>

That is not even remotely Valid, and you are complaining?
document.forms[0].foo;

This does not do anything except reference resolution.
document.forms[0].innerHTML = "";

So you are trying to create an empty `form' element, which is not Valid.

document.write(document.forms[0].foo);
</script>
</body>

Will output:
[object HTMLInputElement]

Works as designed. Good Thing.

This is the result in Firefox. Opera has a different result. The first line:
document.forms[0].foo;

Does affect the result in Firefox.
So you are not even complaining only about Gecko-based UAs in the first
place? Because there is only one Gecko DOM implementation.

I'm not complaining, you are.

That is an incorrect assumption. Apparently you have neither understood
HTML, the DOM, or Windows memory management nor how garbage collection
works: an object that is no longer being referred to is eventually marked
for garbage collection that may or may not come later. It would be very
inefficient would garbage collection take place immediately after an object
would no longer being referred to.

I'm not on Windows. Keeping the browser open for a long time after the
test complets doesn't change the result. I actually went away for 5
minutes and came back and memory usage was the same.
 
T

Thomas 'PointedEars' Lahn

dhtml said:
Closing all of the windows does not result in 0M of memory being used
(real memory).

(Did you notice that I was talking about the process's memory usage?)

Then you have either not waited long enough or you have much greater a
problem than supposed memory leaks from D::createElement. Probably an
extension blocking Firefox's termination (BTDT) as AFAIK Firefox does
not provide the Quick Start feature of Mozilla that keeps it running
in the background (CMIIW).
[...]
I've noticed that createElement leaks. It's obvious with form controls
because the form keeps the control name as a property.

You have noticed nothing of the sort.

The example was shortened for brevity. Validating the html does not
change the result.

True, but changing the markup might.
document.forms[0].foo;

This does not do anything except reference resolution.
document.forms[0].innerHTML = "";

So you are trying to create an empty `form' element, which is not Valid..

document.write(document.forms[0].foo);
</script>
</body>

Will output:
[object HTMLInputElement]

Works as designed.  Good Thing.

This is the result in Firefox. Opera has a different result.

How different?
The first line:
document.forms[0].foo;

Does affect the result in Firefox.

In what way?
I'm not complaining, you are.

I am trying to analyse and rectify your incorrect assumptions instead.
I'm not on Windows.

Right, I assumed from your posting in m.*.jscript before and other
clues in your posting that you were. Incidentally, it would have been
wise to name at least the exact version of Firefox you have been
testing with, including the window framework and operating system it
runs on, and the method of determining the process's memory usage
(that may be flawed).
Keeping the browser open for a long time after the
test complets doesn't change the result. I actually went away for 5
minutes and came back and memory usage was the same.

In case you did not quit the Firefox process: garbage collection is
not just a matter of time. However, as because of your inappropriate
assignment at least one element object was not marked for garbage
collection, and at least one other was created while "destructing" the
other, it would stand to reason that the memory usage did not
decrease, other factors aside.


PointedEars
 
T

Thomas 'PointedEars' Lahn

That doctype triggers standards mode.

That DOCTYPE *declaration* may trigger "standards mode" (in whatever
browser you have tested with), but that does not mean much for
processing and the markup and handling it in DOM operations.
It won't pass the validator.

If desired, a full URI-doctype can be added to achieve the same effect.

The rest of your markup is not Valid as well, so changing the DOCTYPE
declaration is not going to make a considerable difference.
Lets try to not focus on the extraneous noise and keep this focused and
on-topic.

Noticing that the markup of a test case is not Valid is not extraneous
noise.


PointedEars
 
D

dhtml

Thomas said:
(Did you notice that I was talking about the process's memory usage?)

Then you have either not waited long enough or you have much greater a
problem than supposed memory leaks from D::createElement. Probably an
extension blocking Firefox's termination (BTDT) as AFAIK Firefox does
not provide the Quick Start feature of Mozilla that keeps it running
in the background (CMIIW).

Firefox should not terminate when the window is closed. Macs don't work
that way.

This is the result in Firefox. Opera has a different result.
undefined.


How different?
The first line:
document.forms[0].foo;

Does affect the result in Firefox.

If absent, the property will not be retrieved. The result of the second
getting of the |foo| property will be undefined. It seems that the
property will not be cached unless it is first gotten.

Firefox 3.0.1 on Mac.

In case you did not quit the Firefox process: garbage collection is
not just a matter of time. However, as because of your inappropriate
assignment at least one element object was not marked for garbage
collection, and at least one other was created while "destructing" the
other, it would stand to reason that the memory usage did not
decrease, other factors aside.

The initial element would not be collectible.

We've got a page that increases memory and that memory doesn't get
reclaimed. It seems like a memory leak. I can increase it to the point
where Firefox will cause a freeze. entering "400" will do that in Firefox.

Here's Example 1 with valid html (to the same effect) and a |bar| input
that is not referenced in script, plus the elements.foo property.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head><title></title></head>
<body>
<form action=""><input name="foo"><input name="bar"></form>
<pre>
<script type="text/javascript">
document.forms[0].foo;
document.forms[0].elements.foo;

// Clear out form's HTML.
document.forms[0].innerHTML = "";
document.write('foo: ' + document.forms[0].foo+'<br>');
document.write('bar: '+document.forms[0].bar+'<br>');
document.write('elements.foo: '+document.forms[0].elements.foo);
</script>
</pre>
</body>
</html>

Result:
foo: [object HTMLInputElement]
bar: undefined
elements.foo: undefined


Op9.5
foo: undefined
bar: undefined
elements.foo: undefined

After the innerHTML = "", there should be no more |foo| element in the
DOM. It is not gettable through elements collection, but can be gotten
directly off the form.

I disagree that this behavior is a Good Thing.

Garrett
 
D

dhtml

Thomas said:
Noticing that the markup of a test case is not Valid is not extraneous
noise.

No, but stating that I don't understand HTML and so the test is invalid is.

I'm basing a "leak" as memory that does not get freed after a reasonable
amount of time (three minutes).

And now for safari 3.

Lauched, with blank window.
Real Memory Virtual Memory
14.64 MB 172.48 MB

Navigate some google searches, the FAQ page, and then open the test page:
Real Memory Virtual Memory
22.44 MB 190.75 MB

periodicCreate()
400

Result:
Real Memory Virtual Memory
167.38 MB 458.46 MB

destroy()

Result:
Real Memory Virtual Memory
167.38 MB 458.46 MB

Navigate away.
Wait a few minutes.
Real Memory Virtual Memory
99.23 MB 557.16 MB

Minimize Safari.
Wait a few minutes.
Real Memory Virtual Memory
99.23 MB 557.16 MB

Un-minimize Safari, close (only) window, clear cache.
Wait a few minutes.
Real Memory Virtual Memory
91.09 MB 549.89 MB


I observed an increase in memory in Safari 3, and an eventual decrease,
but not to the previous amount of memory.

Garrett
 
N

Nacho

Using Firefox 3.0.1
Well, I did that. I restarted Firefox. and noted the memory usage at "53mb".

I filled in "100" for the text input and clicked "periodicCreate()".

It reached 100 after a less than a minute. I checked the memory usage
again and it was 89.38mb.

I clicked "destroy" and waited a few minutes, watching the memory
increase and Firefox became unresponsive and CPU spiked to 100%.

"destroy" completed several minutes later, and I noted the memory usage
at 85.00mb.

A minute later, real memory remains at around 85mb. I reloaded the page.
85mb. I navigated to google.com. Still at 85mb.
So, Firefox leaks memory with createElement/appendChild.

No, it doesn't. There are, of course, various patterns that will
leak, but this isn't one of them.

What you're noticing is a side-effect of Firefox's memory management,
and this is working as designed. Firefox handles its own memory
allocation, and once it grabs memory from the operating system it
won't free it back to the OS. So if the browser needs more memroy, it
takes it, uses it, and once it no longer needs it anymore (e.g. you
delete your divs, close a tab, etc), Firefox keeps that memory just in
case it needs it later.

The real test for this would be to load your div test, notice the
memory size increase. Then, close your tab, open it up again and run
your div test again. If your Firefox.exe memory size doesn't increase
by 36M again, then you don't have a leak.

If, however, it goes from 53M to 89M, then on the second test attempt
it goes to 125M, then yes you have a legitimate leak.
 
D

dhtml

Nacho said:
What you're noticing is a side-effect of Firefox's memory management,
and this is working as designed. Firefox handles its own memory
allocation, and once it grabs memory from the operating system it
won't free it back to the OS. So if the browser needs more memroy, it
takes it, uses it, and once it no longer needs it anymore (e.g. you
delete your divs, close a tab, etc), Firefox keeps that memory just in
case it needs it later.

The real test for this would be to load your div test, notice the
memory size increase. Then, close your tab, open it up again and run
your div test again. If your Firefox.exe memory size doesn't increase
by 36M again, then you don't have a leak.

(I am on a mac, BTW)
If, however, it goes from 53M to 89M, then on the second test attempt
it goes to 125M, then yes you have a legitimate leak.

Why does Firefox take memory when the page is not closed? After destroy,
there should a references to only one div.

Garrett
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top