...

View Full Version : DOM Insertion Order Leak Model



Basscyst
02-06-2006, 10:30 PM
If I understand this article correctly (refering to the last example on the page: Figure 3. DOM Insertion Order Leak Model)

http://msdn.microsoft.com/library/en-us/IETechCol/dnwebgen/ie_leak_patterns.asp

If I wanted to create a table through the dom, each node's parent node must be appended to the document before the child node can be appended to it. (That's what I got out of it any way)

So I couldn't do this:




var table=document.createElement('table');
var thead=document.createElement('thead');

table.appendChild('thead');
document.documentElement.appendChild(table);



That would create a memory leak in IE because it creates a temporary document element when you append the thead to the table? Is that what it's saying?

I ask this because I do something like the above quite frequently. It seems much more difficult to traverse the dom to append to an element you just appended to the document. It basically means I have to give eveery created element an id, which means I'd only be able to use that element a single time once it's appended, or a name, which is still not very friendly. Or I can traverse the child nodes of the appended object, in any case these methods seem far less efficient. Is it really that bad to do it in the manner I listed in my example?

Thanks,
Basscyst

KC-Luck
02-06-2006, 10:50 PM
the part that gets me in


function CleanMemory()
{
var hostElement = document.getElementById("hostElement");

// Do it a lot, look at Task Manager for memory response

for(i = 0; i < 5000; i++)
{
var parentDiv =
document.createElement("<div onClick='foo()'>");
var childDiv =
document.createElement("<div onClick='foo()'>");

// Changing the order is important, this won't leak
hostElement.appendChild(parentDiv);
parentDiv.appendChild(childDiv);
hostElement.removeChild(parentDiv);
parentDiv.removeChild(childDiv);
parentDiv = null;
childDiv = null;
}
hostElement = null;
}

is the line


hostElement.removeChild(parentDiv);
parentDiv.removeChild(childDiv);

I would think that parentDiv would be a null reference after you have removed it? :)
But the primary focus is just in the way it is appended.
because I can do this


// Changing the order is important, this won't leak
hostElement.appendChild(parentDiv);
parentDiv.appendChild(childDiv);
hostElement.removeChild(parentDiv);
//parentDiv.removeChild(childDiv);
//parentDiv = null;
//childDiv = null;

and memory is still cleaned up properly.

Basscyst
02-06-2006, 11:34 PM
So the example I posted would actually be fine if I had defined the document element as a variable first and appended to that variable?

It just needs to be like this:


var baseobj=document.documentElement;
var table=document.createElement('table');
var thead=document.createElement('thead');

baseobj.appendChild(table)
table.appendChild('thead');

etc . .

Would I be correct in saying:

All parent elements of an object must be appended to the document before any objects can be appended to the object?

If that's the case I think I might be in pretty good shape with the exception of some places where I might be appending text nodes to tds before the td is attached to the tr.



hostElement.removeChild(parentDiv);
parentDiv.removeChild(childDiv);


Yeah that is weird, I wouldn't logically write it in that order. . .I would assume though that the variable still references the object even though it is no longer attached to the document, because doesn't removeChild() return the object removed?

Thanks,
Basscyst

KC-Luck
02-07-2006, 02:03 AM
So the example I posted would actually be fine if I had defined the document element as a variable first and appended to that variable?

It just needs to be like this:


var baseobj=document.documentElement;
var table=document.createElement('table');
var thead=document.createElement('thead');

baseobj.appendChild(table)
table.appendChild('thead');

etc . .


You would not want to append directly to the documentElement.
documentElement = HTMLElement (root)
you would want to append to the document.getElementsByTagName("body").item(0);
but, yes you are on the right track.


Would I be correct in saying:

All parent elements of an object must be appended to the document before any objects can be appended to the object?

If that's the case I think I might be in pretty good shape with the exception of some places where I might be appending text nodes to tds before the td is attached to the tr.

Yes, that is how I have understood the article as well.
Append the node to document, then append other children to it.
Not the other way around. Don't work with the newly created element in memory, via appending children, before you actually "put" it in the DOM.

And, yes removeChild(oldNode) does return a reference to the removed element. But, was just weird to then remove children of it, after it was removed? It seems that upon removal, the children of removed node would be gone already :)

Just don't like to add extra "bloat" if not a truely needed line of code :)

Kor
02-07-2006, 10:50 AM
You would not want to append directly to the documentElement.
documentElement = HTMLElement (root)

Absolutelly. Appending a table to the documentElement will append the table after the </body> tag, which, of course, you would not to...

Speaking about leaks... I don't think that in this case the leak (and, sure, why am I wonder? This kinda leak is only in IE... :D ) is significant. Let's say you create a table (first temporary element) than a tbody(second), than a row (third) thane a cell(forth). Now append the cell (the appending will reduce the temporary objects to three, as the moment you append it, it is not a temporary one). Append the row (2 temporary remains: table and tbody)... and so on...

I might be wrong, but this is the way I see things... Of course, if you append each element to it's parent the next moment after creating, that it will keep the temporary element to maximum 1, and only from time to time, which will certainly speen the process, but who, for God's sake, wants to create a table with 5000 cells in order to sense the difference? :D

KC-Luck
02-07-2006, 08:52 PM
keep in mind, if you change:

document.createElement("<div onClick='foo()'>");
to

document.createElement("div");
you will have no leak, either way you append them :)

liorean
02-07-2006, 09:01 PM
They're really not explaining the problem that well. It's got to do with the creation of a closure by creating that event handler.

Basscyst
02-07-2006, 10:32 PM
keep in mind, if you change:

document.createElement("<div onClick='foo()'>");
to

document.createElement("div");
you will have no leak, either way you append them :)

Oh. . .if that's the case it's rare that it's an issue, only when needing to set certain attributes in ie. In any case I understand. :)

and yeah, I know i wouldn't append to the documentElement, I was working with an xml document when I wrote this and my brain leaked over. :p

Thanks,
Basscyst

KC-Luck
02-07-2006, 11:25 PM
They're really not explaining the problem that well. It's got to do with the creation of a closure by creating that event handler.
if they want the full explanation basscyst has offered up the URL at topic start.

This leak deserves clarification, because our workaround goes against some best practices in IE. The key points to understand about the leak are that DOM elements are being created with scripts already attached. This is actually crucial to the leak, because if we create DOM elements that don't contain any script and attach them together in the same manner we don't have a leak problem. This gives rise to a second workaround that might be even better for larger subtrees (in the example we only have two elements, so building the tree off the primary DOM isn't a performance hit). The second workaround would be to create your elements with no scripts attached initially so that you can safely build your subtree. After you've attached your subtree to the primary DOM, go back and wire up any script events at that point. Remember to follow the principles for circular references and closures so you don't cause a different leak in your code as you hook up your events.

yes, it is a rare case that such events are wired up as such, so I never give much second-thought to this type of memory leak, but a good one to know, and observe! ;)

jkd
02-08-2006, 01:37 AM
A somewhat-related note:

Appending nodes to the live DOM (the document) will redraw the affected area, which takes some amount of CPU power. If you're creating many nodes, it is much faster to create the structure in memory, and then append the single parent node to the live DOM, instead of potentially thousands of additions (and thousands of redraws). Even if this way leaks a little in IE, the speed it makes up otherwise I would consider a worthy tradeoff.

liorean
02-08-2006, 01:55 AM
KC-Luck: Yeah, that's exactly what I was referring to. It's terribly inspecific. It doesn't in any degree explain what the environment that the closure closes over actually is (I'm pretty sure it's not a straight lexical closure, since you could null all locals to get rid of it in that case, and the code that leaks actually does just that.), nor do they explain that it is in fact just a hidden creation of a closure.

Looking closer, I see that they actually do specify it, but not in those words. They explain the scope trickery just before the diagram.

jkd: Nah, the problem really lies in that people are using the proprietary marked-up-tag syntax to attach the event instead of attaching the event separately. Using the standard DOM tagname-only syntax and attaching the event separately effectively removes this odd kind of closure creation, giving you just a single kind of closures to deal with.



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum