
Have you been writing web applications for a long time, have you already seen everything and aren't you afraid of anything else? Then turn off the lights and sit down, I want to tell you a tale for the night.
Once in one big-big city, in one big-big IT company, they tested one big-big project in one very used browser. And they found memory leaks there. Big-big. Straight shortly before release.
')
And it would be no wonder if the developers were completely stupid. But no, the developers by heart knew
"Understanding and Solving Internet Explorer Leak Patterns" . Cyclic links were broken, closures were not used, events were treated with due respect, and handlers were not forgotten to remove. Why, only it did not save from leaks.
Discalimer: All entities, events and code snippets below are fictional. All matches are random.Well, what was a good fellow to do? Once again they sat down to comb their components and see if they missed something, they started to launch various
programs and torture the answer from Google. But the programs did not help, the wise Googol was also silent. I had to roll up my sleeves and search for a line where the infection lurked. And there was a malicious line, and it looked like this:
var cell = tableEl.firstChild.rows[0].cells[0];
And the developers came in bewilderment, they had to study the problem and solve it. What they managed.
And then the tale ends and the code begins to show the problem:
<!DOCTYPE HTML>
<html>
<body>
<span id="count">0</span> \<input id="num" value="1000" /\>
<input type="button" value="GO!" onclick="execute()" />
<hr />
<div id="test"></div>
</body>
<script type="text/javascript">
var count = 0;
function execute()
{
var val = document.getElementById('num').value;
for (var i = 0; i < val; i++)
{
var domEl = document.getElementById('test');
domEl.innerHTML = '<table><tbody><tr><td>A1</td></tr><tr><td>B1</td></tr></tbody></table>';
domEl.firstChild.insertRow(0);
domEl.removeChild(domEl.firstChild);
count++;
}
document.getElementById('count').innerHTML = count;
};
</script>
</html>
Run Process Explorer and click “GO!” Several times - 10 megabytes escapes with a click:

The leak is hidden in the line
domEl.firstChild.rows[0]
which you can verify by simply
domEl.firstChild.rows[0]
it out. And the bug only appears in the IE8 Standards mode, the IE7 and IE8 Quirks modes are not affected by this disease.
Replacing with domEl.firstChild.firstChild or with getElementById helps fix the leak, but if there are hundreds of such lines throughout the project? Maybe there is a less time consuming option?
Let's try to estimate how the Hindus from Microsoft managed to achieve this effect. Obviously, collections of columns and cells are created only when used, the solution is quite sound. And when the table is uncoupled from the DOM of the document tree, they are not destroyed - it is also quite understandable and reasonable. Only here in the garbage collector, for some reason, they forgot to take into account the cyclic references that are formed.
The hypothesis can be tested. If it is a matter of creating collections, there will be leaks in other cases when indexes are used. Replace rows [0] with insertRow (0). Bingo! Leakage preserved.
Now the way to fight is clear. Once it’s a matter of circular references between the DOM elements TBODY and TD, it means you just need to remove all the descendants of TBODY:
while(domEl.firstChild.firstChild) {
while(domEl.firstChild.firstChild.firstChild)
domEl.firstChild.firstChild.removeChild(domEl.firstChild.firstChild.firstChild);
domEl.firstChild.removeChild(domEl.firstChild.firstChild);
}
It worked, but this is still not enough for happiness; it is rather dreary to take care of the correct removal of the table in the component separately, and forget something for long. You can, of course, automate the process, use something like getElementsByTag ('TABLE') ... But you don’t need to, as there is a magic innerHTML property that does everything for us:
Replace
domEl.removeChild(domEl.firstChild)
with
domEl.innerHTML = ''
We are checking. Works.
To summarize:
This type of leakage is not documented; it occurs only in the IE8 Standards mode when using methods and properties of DOM table objects that use row or column indexes. (.rows [], .cells [], insertRow (), etc.)
The best way to work around a problem is to not use these methods and properties.
Alternative - make sure that the DOM elements of all rows of the table are disconnected from their ancestor, or use the simplest option:
innerHTML = ''