473,396 Members | 1,970 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

more details on jscript memory leak from DOM object references

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_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_reac hable (expect:
FAIL)</div>
<div onclick="leak_test_attachEvent(this)">leak_test_at tachEvent
(expect: FAIL)</div>
<br>
<div onclick="CollectGarbage()">CollectGarbage (expect: never does
anything)</div>
</body>
</html>
Jul 20 '05 #1
4 5526
"Mark D. Anderson" <md*@discerning.com> wrote in message
news:b9**************************@posting.google.c om...
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:
<snip URL of previous discussion via groups.google.com>
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.


I am not going to have time to examine your test page in detail tonight
(and maybe not tomorrow either) to see if and why you think your results
are different to mine (if you are going to claim your results contradict
my conclusions it would have saved a bit of time if you had stated the
specific conclusions that you think are contradicted). I should be able
to find time to look at your page in detail over the weekend and let you
know what I think.

However, did you notice early in the previous thread Jim Ley implying
that just repeatedly refreshing the same page may give the impression of
a memory leak in IE when navigating away from the page would free the
memory. He specifically agreed with my suggestion that any page wishing
to demonstrate a real problem in IE would have to cycle between at least
two distinct pages.

While I would not take anyone's word as gospel, when it comes to
obscurer details of browser behaviour I would always pay close attention
to what Jim has to say. Accordingly my tests used two or three pages and
cycled between them. As a result I am certain that they do demonstrate
the memory leaks I described, though I may have concluded that the
problem was more general than it actually is. But I will be
incorporating your code into Multi-page examples for testing.

Giving your script a cursory glance, specifically the -
leak_test_global - function, I don't think that it demonstrates that
references to global objects do not produce leaks as the DIV element
does not have a reference to the global object and the closure formed by
assigning the event handling function does not contain a reference to
the global object. If you replace the event handling function with an
expando property that refers to the global object, so that the reference
is circular:-

function leak_test_global(node) {
test_start('global');
node.expando1 = myglobal;
myglobal.push(node); // make circular
}

- I think you will find that it does leak.

Richard.
Jul 20 '05 #2
"Mark D. Anderson" <md*@discerning.com> wrote in message
news:b9**************************@posting.google.c om...
<snip>
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). <snip>function leak_test_domref(node) {
test_start('domref');
var mylocal = myglobal;
node.onclick = function() {
alert("length: " + mylocal.length)};
}

<snip>

I am getting hooked (and I should be sleeping). This one is not a
one-way reference. Assigning the inner function to onclick is forming a
closure. That closure creates a circular reference becase its - node -
parameter is a reference to the DIV and the DIV has a reference to the
inner function. Those circular references preserve the closure and the
closure contains a reference to - myglobal -, preserving it. Try nulling
the - node - reference at the end of the function; breaking the circle.

function leak_test_domref(node) {
test_start('domref');
var mylocal = myglobal;
node.onclick = function() {
alert("length: " + mylocal.length)};
node = null;
}

leak_test_reachable - and - leak_test_attachEvent - have similar
problems.

Richard.
Jul 20 '05 #3
"Mark D. Anderson" <md*@discerning.com> wrote in message
news:b9**************************@posting.google.c om...
FYI I have a new version at
http://www.discerning.com/js/testleak.html with corrections
reflecting your comments, some additions, and general cleanup.
Would you mind removing my e-mail address from that page. As I never
respond to unsolicited e-mail from total strangers it can server no
purpose but encourage spam.
(if you are going to claim your results contradict my
conclusions it would have saved a bit of time if you had stated
the specific conclusions that you think are contradicted).


Sorry. But now with your corrections, I'm not sure there are
contradictions anymore :).


Yes, I do not see anything that contradicts my original conclusions.
Circular references between DOM elements and JS objects do prevent
garbage collection on IE and one-way references do not.
I should say too that I started with your posting not to pick
on you, but because it was the best I found anywhere.
I didn't think that you did, and I am not arrogant enough to think that
I could not have been wrong in the first place. I just would have liked
to be able to narrow down to the specifics quickly.

<snip>Btw, i'm guessing that CollectGarbage() on IE does something
similar to what navigating to a new site would do;
neither seems to have any consequence for these tests.
Without documentation your guess is as good as mine. Certainly
CollectGarbage is of no help with the memory leak problem.

<snip>
function leak_test_global(node) {
test_start('global');
node.expando1 = myglobal;
myglobal.push(node); // make circular
}

- I think you will find that it does leak.


I have added a new test, leak_test_globalexpando which is your
version above, and yes, it does leak.

What I don't understand though is what is different about
globals from locals that mean that a global is not trapped
in the closure below:


Function local variables and parameters are stored in the execution
context of a function call as properties of an internal object referred
to in the ECMA spec as the "variable" object. Because inner functions
have access to those parameters and variables after the completion of
the execution of their outer function the "variable" object (at the very
least) must continue to exist when a closure is formed. Thus and
references to DOM Elements held on the "variables" object must also
continue to exist.

Occurrences of an identifier for a global variable within a function do
not effect the "variable" object and will eventually be scope-resolved
against the global object. So no references to the global variables are
preserved within a closure unless they are also assigned to a local
variable or parameter.

A closure might best be perceived as a structure of JavaScript objects,
at minimum: a function object (the inner function) and a "variable"
object, with the function object having a property (internal) that
refers (possibly indirectly) to the "variable" object.

<snip> i also don't understand by node.expando1 = myglobal traps
myglobal, but using node.onclick = function() {...expression
using myglobal...} does not.

<snip>

Your original function did produce a memory leak because the - node -
parameter referred to the DOM Element and assigning the inner function
to the event handler formed a closure. However, no references to that
global object (with the big string) were trapped in the closure (as
explained above) so the leek was too small be exposed by this test
method.

Having the DOM Element directly hold a reference to the global object
while the global object held a reference to the Element produced a
circular reference that did encompass the big string. No closure was
formed in that case but it does demonstrate that global object have no
special role in the context of this problem; all JavaScript objects
(including the internal "variable" object) can form one point in an
unbreakable circular reference on IE.

I will not have an opportunity to look at your new page in detail today
(maybe tomorrow), I will let you know if anything else occurs to me.

Richard.
Jul 20 '05 #4
> Would you mind removing my e-mail address from that page. As I never
respond to unsolicited e-mail from total strangers it can server no
purpose but encourage spam.
Done; now spammers will have to be satisfied with using one of your
1300+ posts
to usenet :).

Occurrences of an identifier for a global variable within a function do
not effect the "variable" object and will eventually be scope-resolved
against the global object. So no references to the global variables are
preserved within a closure unless they are also assigned to a local
variable or parameter.


Thanks; that helps.
I just found this in 262-3 section 10.1.3 which is consistent
with what you say:

Every execution context has associated with it a variable object.
Variables and functions declared in the source text are added as
properties of the variable object. For function code, parameters
are added as properties of the variable object.

Reading some of the surrounding text, you are definitely correct that
global code
(and global variables) are a distinct case from function code, at any
level of nesting.

-mda
Jul 20 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Tony Van | last post by:
I think I know what it is-- objects not being set to nothing when the program is finished with them How can I tell if I have it? How can I code to avoid it? Thanks
21
by: Rabbit63 | last post by:
Hi: I want to show a set of records in the database table on the clicnt browser. I have two ways to do this (writen in JScript): 1.The first way is: <% var sql = "select firstname from...
1
by: mark.engelberg | last post by:
I am having trouble identifying the source of a memory leak in a Windows Python program. The basic gist is as follows: 1. Generate a directed graph (approx. 1000 nodes). 2. Write the graph to a...
7
by: George Gre | last post by:
Hi, I wrote a c# programme that listens to incoming TCP requests and services them. This programme is meant to be running as long as the server its installed on is running. So we assume for...
1
by: anandav2001 | last post by:
Hello developers, I have created an executable(system tray application) in VS.net 2003 using VB.net. My app was taking 30 MB memory(since some web services call are there which happens for each...
3
by: Mitch Freed | last post by:
I have been using the JScript eval() method from C# to evaluate conditional logic at run-time. I found that implementing a solution similar to the example posted at:...
2
by: danny.dion | last post by:
Hi ! I have a question about JScript : I have an object class wich dynamically creates a control in the page. Then it binds an event to that control, pointing on one of its methods (the...
3
by: Jim Land | last post by:
Jack Slocum claims here http://www.jackslocum.com/yui/2006/10/02/3-easy-steps-to-avoid-javascript- memory-leaks/ that "almost every site you visit that uses JavaScript is leaking memory". ...
22
by: Frank Rizzo | last post by:
I have an object tree that is pretty gigantic and it holds about 100mb of data. When I set the top object to null, I expect that the .NET framework will clean up the memory at some point. ...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.