M
Mark D. Anderson
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/[email protected]
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_global
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_2nodes
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_domref
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_reachable
answer: Yes.
question: Does Node.attachEvent also leak?
test: leak_test_attachEvent
answer: Yes
-mda
------- testleak.html -----
<html>
<!-- Tests of internet explorer leaks. See discussion in
comp.lang.javascript, 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(1000000, largeText(1000));
//alert("myglobal.big.length=" + myglobal.big.length);
function test_start(name) {
alert("performing test '" + name + "'");
}
function leak_test_global(node) {
test_start('global');
node.onclick = function() {alert("length: " + myglobal.length)};
myglobal.push(node); // make circular
}
function leak_test_local(node) {
test_start('local');
var mylocal = myglobal;
node.onclick = function() {alert("length: " + mylocal.length)};
mylocal.push(node);
}
function leak_test_2nodes(node) {
test_start('2nodes');
var mylocal = myglobal;
var node2 = document.createElement('div');
document.body.appendChild(node2);
node.onclick = function() {alert("length: " + mylocal.length)};
mylocal.push(node2);
}
function leak_test_domref(node) {
test_start('domref');
var mylocal = myglobal;
node.onclick = function() {alert("length: " + mylocal.length)};
}
function leak_test_jsref(node) {
test_start('jsref');
myglobal['foobar'] = node;
}
function leak_test_reachable(node) {
test_start('reachable');
var mylocal = {stuff : {bother: myglobal}};
node.onclick = function() {alert("length: " +
mylocal.stuff.bother.length)};
mylocal['mynode'] = node;
}
function leak_test_attachEvent(node) {
test_start('attachEvent');
if (!node.attachEvent) {alert("no attachEvent"); return;}
var mylocal = myglobal;
node.attachEvent('onclick', function() {alert("length: " +
mylocal.length)});
// reference seems unnecessary
//mylocal.push(node);
}
</script>
</head>
<body>
<div onclick="leak_test_global(this)">leak_test_global (expect:
PASS)</div>
<div onclick="leak_test_local(this)">leak_test_local (expect:
FAIL)</div>
<div onclick="leak_test_2nodes(this)">leak_test_2nodes (expect:
FAIL)</div>
<div onclick="leak_test_domref(this)">leak_test_domref (expect:
FAIL)</div>
<div onclick="leak_test_jsref(this)">leak_test_jsref (expect:
PASS)</div>
<div onclick="leak_test_reachable(this)">leak_test_reachable (expect:
FAIL)</div>
<div onclick="leak_test_attachEvent(this)">leak_test_attachEvent
(expect: FAIL)</div>
<br>
<div onclick="CollectGarbage()">CollectGarbage (expect: never does
anything)</div>
</body>
</html>
memory leak
in jscript (internet explorer) when there are "circular" references
between
DOM objects and (real) jscript objects:
http://groups.google.com/[email protected]
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_global
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_2nodes
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_domref
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_reachable
answer: Yes.
question: Does Node.attachEvent also leak?
test: leak_test_attachEvent
answer: Yes
-mda
------- testleak.html -----
<html>
<!-- Tests of internet explorer leaks. See discussion in
comp.lang.javascript, 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(1000000, largeText(1000));
//alert("myglobal.big.length=" + myglobal.big.length);
function test_start(name) {
alert("performing test '" + name + "'");
}
function leak_test_global(node) {
test_start('global');
node.onclick = function() {alert("length: " + myglobal.length)};
myglobal.push(node); // make circular
}
function leak_test_local(node) {
test_start('local');
var mylocal = myglobal;
node.onclick = function() {alert("length: " + mylocal.length)};
mylocal.push(node);
}
function leak_test_2nodes(node) {
test_start('2nodes');
var mylocal = myglobal;
var node2 = document.createElement('div');
document.body.appendChild(node2);
node.onclick = function() {alert("length: " + mylocal.length)};
mylocal.push(node2);
}
function leak_test_domref(node) {
test_start('domref');
var mylocal = myglobal;
node.onclick = function() {alert("length: " + mylocal.length)};
}
function leak_test_jsref(node) {
test_start('jsref');
myglobal['foobar'] = node;
}
function leak_test_reachable(node) {
test_start('reachable');
var mylocal = {stuff : {bother: myglobal}};
node.onclick = function() {alert("length: " +
mylocal.stuff.bother.length)};
mylocal['mynode'] = node;
}
function leak_test_attachEvent(node) {
test_start('attachEvent');
if (!node.attachEvent) {alert("no attachEvent"); return;}
var mylocal = myglobal;
node.attachEvent('onclick', function() {alert("length: " +
mylocal.length)});
// reference seems unnecessary
//mylocal.push(node);
}
</script>
</head>
<body>
<div onclick="leak_test_global(this)">leak_test_global (expect:
PASS)</div>
<div onclick="leak_test_local(this)">leak_test_local (expect:
FAIL)</div>
<div onclick="leak_test_2nodes(this)">leak_test_2nodes (expect:
FAIL)</div>
<div onclick="leak_test_domref(this)">leak_test_domref (expect:
FAIL)</div>
<div onclick="leak_test_jsref(this)">leak_test_jsref (expect:
PASS)</div>
<div onclick="leak_test_reachable(this)">leak_test_reachable (expect:
FAIL)</div>
<div onclick="leak_test_attachEvent(this)">leak_test_attachEvent
(expect: FAIL)</div>
<br>
<div onclick="CollectGarbage()">CollectGarbage (expect: never does
anything)</div>
</body>
</html>