About a month ago Richard Cornford did an interesting analysis of a
memory leak
in jscript (internet explorer) when there are "circular" references
between
DOM objects and (real) jscript objects:
http://groups.google.com/groups?selm...ws.demon.co.uk
This message summarizes some testing I've done and their results.
These results somewhat contradict Cornford's conclusions; I haven't
analyzed his
test page to come to an explanation.
Below is an html test page so that anyone can (attempt to) reproduce
my results.
To test:
- Bring up task manager and a fresh IE process on this web page.
- Click on one of the div links, refresh the page, repeat, and watch
the process size.
I have been doing 3 refresh/click sequences per div.
Because the leaked object is at least 1Mbyte, it should be apparent
when the leak happens.
(I see leaks in increments of 2Mbyte, presumably because the
characters
in the 10^6 long string are stored as 2 bytes each, for unicode.)
I did my testing on IE 6.0.
I also ran the tests on Mozilla 1.1. I discovered that the largeText
function
is much slower to execute on Mozilla than IE, but I found no leaks.
question: Does the leak occur if an event handler function refers to
only global js objects?
test: leak_test_globa l
answer: No.
Apparently references to global variables (scope "window") are not
implemented in
the same way as lexical closures on local variables.
question: Does the leak happen if an event handler function refers to
a local js object?
test: leak_test_local
answer: Yes.
question: Does the leak happen without true circularity, just mutual
references somewhere between DOM and JS?
test: leak_test_2node s
answer: Yes (true circularity is not required).
question: Does the leak occur with just a one-way reference from a DOM
object to a JS object?
test: leak_test_domre f
answer: Yes (not even mutual reference is required).
question: Does the leak occur with just a one-way reference from a JS
object to a DOM object?
test: leak_test_jsref
answer: No.
question: Are all JS objects leaked which are reachable by transitive
reference from one bound by lexical closure?
test: leak_test_reach able
answer: Yes.
question: Does Node.attachEven t also leak?
test: leak_test_attac hEvent
answer: Yes
-mda
------- testleak.html -----
<html>
<!-- Tests of internet explorer leaks. See discussion in
comp.lang.javas cript, 2003-7-24 -->
<head>
<script type="text/javascript">
function largeText(len, s) {
if (!s) s = '0123456789';
var a = [];
for(var i=len/s.length;i--;) a.push(s);
return a.join('');
}
var myglobal = [];
myglobal.big = largeText(10000 00, largeText(1000) );
//alert("myglobal .big.length=" + myglobal.big.le ngth);
function test_start(name ) {
alert("performi ng test '" + name + "'");
}
function leak_test_globa l(node) {
test_start('glo bal');
node.onclick = function() {alert("length: " + myglobal.length )};
myglobal.push(n ode); // make circular
}
function leak_test_local (node) {
test_start('loc al');
var mylocal = myglobal;
node.onclick = function() {alert("length: " + mylocal.length) };
mylocal.push(no de);
}
function leak_test_2node s(node) {
test_start('2no des');
var mylocal = myglobal;
var node2 = document.create Element('div');
document.body.a ppendChild(node 2);
node.onclick = function() {alert("length: " + mylocal.length) };
mylocal.push(no de2);
}
function leak_test_domre f(node) {
test_start('dom ref');
var mylocal = myglobal;
node.onclick = function() {alert("length: " + mylocal.length) };
}
function leak_test_jsref (node) {
test_start('jsr ef');
myglobal['foobar'] = node;
}
function leak_test_reach able(node) {
test_start('rea chable');
var mylocal = {stuff : {bother: myglobal}};
node.onclick = function() {alert("length: " +
mylocal.stuff.b other.length)};
mylocal['mynode'] = node;
}
function leak_test_attac hEvent(node) {
test_start('att achEvent');
if (!node.attachEv ent) {alert("no attachEvent"); return;}
var mylocal = myglobal;
node.attachEven t('onclick', function() {alert("length: " +
mylocal.length) });
// reference seems unnecessary
//mylocal.push(no de);
}
</script>
</head>
<body>
<div onclick="leak_t est_global(this )">leak_test_gl obal (expect:
PASS)</div>
<div onclick="leak_t est_local(this) ">leak_test_loc al (expect:
FAIL)</div>
<div onclick="leak_t est_2nodes(this )">leak_test_2n odes (expect:
FAIL)</div>
<div onclick="leak_t est_domref(this )">leak_test_do mref (expect:
FAIL)</div>
<div onclick="leak_t est_jsref(this) ">leak_test_jsr ef (expect:
PASS)</div>
<div onclick="leak_t est_reachable(t his)">leak_test _reachable (expect:
FAIL)</div>
<div onclick="leak_t est_attachEvent (this)">leak_te st_attachEvent
(expect: FAIL)</div>
<br>
<div onclick="Collec tGarbage()">Col lectGarbage (expect: never does
anything)</div>
</body>
</html>