garbage collection eligibility

P

Peter Michaux

Hi,

Below is an example from Flanagan's fifth edition Ajax chapter. It
looks to me like the "request" variable might be eligible for garbage
collection after this function executes. This would mean the request
object is gone by the time the server responds. Or does the closure of
the anonymous function make it so that the request object can't be
garbage collected?


HTTP.getText = function(url, callback) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200)
callback(request.responseText);
}
request.open("GET", url);
request.send(null);
};


Note that HTTP.newRequest() is just a factory that cycles through the
options for creating either an XMLHttpRequest object or an
ActiveXObject.

Thank you,
Peter
 
R

Richard Cornford

Peter said:
Below is an example from Flanagan's fifth edition Ajax chapter.
It looks to me like the "request" variable might be eligible
for garbage collection after this function executes. This
would mean the request object is gone by the time the server
responds. Or does the closure of the anonymous function make
it so that the request object can't be garbage collected?


HTTP.getText = function(url, callback) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200)
callback(request.responseText);
}
request.open("GET", url);
request.send(null);
};
<snip>

That is precisely what happens. The anonymous function object has an
internal [[Scope]] property that refers to the scope chain structure of
the execution context in which the function was created (as a result of
the evaluation of the function expression as part of the assignment to -
request.onreadystatechange -). That scope chain structure includes (so
must refer to) the Activation/Variable object from the containing
execution context, and that Activation/Variable object has a "request"
property that refers to the xml http request object.

Th anonymous function object cannot be garbage collected because it can
still be called, when (if) it is called it will been the structure
referred to by its [[Scope]] property in order to form the scope chain
for its execution context, so none of the objects on that chain can be
garbage collects, and neither can any of the objects refereed to be
properties of the objects on the scope chain.

Incidentally, the fact that - request.onreadystatechange - refers to the
function object, the function object refers to the Activation/Variable
object (through its scope chain) and the Activation/Variable object
refers to the xml http request object gives a circular chain of
references and so will provoke IE's memory leak problem. The fact that
the - onreadystatechange - handler does nothing to break this circular
reference actually mans that on IE none of the objects involved will be
garbage collected until the browser shuts down. Making this yet another
unwise example form Flanagan (there is also a missing third argument to
the - open - call, which is an issue in some xml http request object
implementations, and implies synchronous requests in others (implicit
Undefined type-converts to false), for which - onreadystatechange -
handers are unnecessary, and in some implementations they will not be
called).

Richard.
 
P

Peter Michaux

Hi Richard,

Thank you for the explanation. I would like to understand this problem
thoroughly and if you have time for a few more questions I would
appreciate knowing what you think.


Richard said:
Peter said:
Below is an example from Flanagan's fifth edition Ajax chapter.
It looks to me like the "request" variable might be eligible
for garbage collection after this function executes. This
would mean the request object is gone by the time the server
responds. Or does the closure of the anonymous function make
it so that the request object can't be garbage collected?


HTTP.getText = function(url, callback) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200)
callback(request.responseText);
}
request.open("GET", url);
request.send(null);
};
<snip>

That is precisely what happens. The anonymous function object has an
internal [[Scope]] property that refers to the scope chain structure of
the execution context in which the function was created (as a result of
the evaluation of the function expression as part of the assignment to -
request.onreadystatechange -). That scope chain structure includes (so
must refer to) the Activation/Variable object from the containing
execution context, and that Activation/Variable object has a "request"
property that refers to the xml http request object.

I haven't read about the "Activation/Variable" term before. Is that
slashed because different browsers call it different things?


[snip]
Incidentally, the fact that - request.onreadystatechange - refers to the
function object, the function object refers to the Activation/Variable
object (through its scope chain) and the Activation/Variable object
refers to the xml http request object gives a circular chain of
references and so will provoke IE's memory leak problem.

So in a non-IE browser, what is stopping this circle of objects to be
garbage collected as a group? That is, what is pointing to one of it's
several objects?

The fact that
the - onreadystatechange - handler does nothing to break this circular
reference actually mans that on IE none of the objects involved will be
garbage collected until the browser shuts down. Making this yet another
unwise example form Flanagan (there is also a missing third argument to
the - open - call, which is an issue in some xml http request object
implementations, and implies synchronous requests in others (implicit
Undefined type-converts to false), for which - onreadystatechange -
handers are unnecessary, and in some implementations they will not be
called).

I assume my goal is to break this circle and am not sure if I have this
correct. Staying with the Flanagan example, I now have the following

HTTP.getText = function(url, callback) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200) {
callback(request.responseText);
}
alert(typeof request.onreadystatechange);
delete request.onreadystatechange;
alert(typeof request.onreadystatechange);
}
}
request.open("GET", url, true);
request.send(null);
};

In Firefox 1.5 both alerts are display "object".
In Internet Explorer 6 both alerts display "unknown".
In Opera 9 and Safari 2 both alerts display "function".

I expected the first alert to display "function" and the second to
display "undefined". Do you know what is happening here?

Thank you,
Peter
 
M

Michael Winter

Peter Michaux wrote:

[snip]
I haven't read about the "Activation/Variable" term before. Is that
slashed because different browsers call it different things?

The same object is used for two things. The activation object is created
when control enters a new execution context - specifically, when a
function is called - and is assigned an object with the property name,
arguments (that is, the arguments object is created). The activation
object is then used as the variable object, which is involved in
variable instantiation.

When entering any execution context (global and eval code are the other
two possibilities) function and variable declarations are processed, and
each identifier creates a new property of the variable object. Function
declarations are processed first in source order and references to the
function objects resulting from each declaration are assigned to those
properties. The initialisation of variables doesn't occur until those
initialisers are evaluated during execution (which may not happen at
all), so each property will have the value, undefined.

When calling functions, and before function declarations in the body of
that function are processed, the formal arguments and the respective
values passed are added left-to-right as properties of the variable
object. The variable object is also added to the top of the scope chain
so that its properties are involved in identifier resolution.

The activation/variable object is a specification device: code can never
access it, and it doesn't even have to exist. However, if that is the
case, the implementation must still provide equivalent behaviour.

The article in the FAQ notes on closures explains this process in more
detail (and a little more precisely), as does section 10 of ECMA-262.
So in a non-IE browser, what is stopping this circle of objects to be
garbage collected as a group? ...

That would be my question, too. I would have assumed that a race
condition existed: the circular reference is in isolation so once it's
discovered when garbage collection begins, it can be removed. This may
happen before or after the response is received.

The host may make an effort to hold on to the object until the request
completes, but that would seem to be a special case.

[snip]
I assume my goal is to break this circle
Yes.

and am not sure if I have this correct. ...

You are taking the right general approach - removing the event listener
- but I would disagree with the actual implementation.
HTTP.getText = function(url, callback) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200) {
callback(request.responseText);
}
alert(typeof request.onreadystatechange);
delete request.onreadystatechange;

Calling the delete operator for properties of host objects is somewhat
hit and miss, not least because deletion can be disallowed. Moreover,
I've found that IE tends to raise exceptions (certainly with DOM nodes).
Instead, assign null to the property.

[snip]

Mike
 
R

Richard Cornford

Peter Michaux wrote:
Richard Cornford wrote:
That is precisely what happens. The anonymous function
object has an internal [[Scope]] property that refers
to the scope chain structure of the execution context
in which the function was created (as a result of the
evaluation of the function expression as part of the
assignment to - request.onreadystatechange -). That
scope chain structure includes (so must refer to) the
Activation/Variable object from the containing execution
context, and that Activation/Variable object has a
"request" property that refers to the xml http request
object.

I haven't read about the "Activation/Variable" term before.

ECMA 262 3rd Ed. Section 10.
Is that slashed because different browsers call it different
things?

It is slashed because the Activation object becomes (is used as) the
Variable object for a function's execution context. (Global execution
contexts do not need, so do not have, and Activation object but the
global object is then used as the Variable object)

Flanagan refers to this object as the "call" object ("JavaScript the
definitive guide", 5th Ed. Chapter 8, Section 8.8.2, page 142), and
briefly mentions that it is the ECMAScript "activation" object.
Unfortunately the succession of questions to this group over the years in
which readers of Flanagan have confused his "call" object with the object
providing the - this - value when a function is called demonstrates the
wisdom of using such ill defined terminology.

[snip]
Incidentally, the fact that - request.onreadystatechange -
refers to the function object, the function object refers
to the Activation/Variable object (through its scope chain)
and the Activation/Variable object refers to the xml http
request object gives a circular chain of references and so
will provoke IE's memory leak problem.

So in a non-IE browser, what is stopping this circle of
objects to be garbage collected as a group?
That is, what is pointing to one of it's several objects?

Probably whatever mechanism links the HTTP response from the request with
the xml http request object that made the original request. Presumably
the browser must have a way of referring to the xml http request object
(directly, or more directly) so that it can do something appropriate when
the response arrives.
I assume my goal is to break this circle and am not sure if
I have this correct.

Yes, you should explicitly break the circle else IE will not free the
memory involved until the browser closes (at least with the ActiveX
version of xml http request objects, the native version in IE 7 may not
exhibit the same problem (though I expect it will as it is probably just
a wrapper around the ActiveX object that allows it to be used when
ActiveX is disabled/restricted in the browser)).

There are two good candidates for breaking the circle; the "request"
property of the Activation/Variable object refers to the xml http request
object, so could have - null - assigned to it after the callback function
processes the response, and - onreadystatechange - property of the xml
http request object refers to the anonymous function object, and could be
re-assigned. Unfortunately assigning a non-function to the -
onreadystatechange - property does not work in IE (an allowable quirk of
some ActiveX objects), so the value assigned to break the circle would
have to be a function, but that function could be a simple (and
re-useable) dummy that just did not have a scope chain that held any
references to any xml http request obejcts.
Staying with the Flanagan example, I now have the
following

HTTP.getText = function(url, callback) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200) {
callback(request.responseText);
}
alert(typeof request.onreadystatechange);
delete request.onreadystatechange;
alert(typeof request.onreadystatechange);
}
}
request.open("GET", url, true);
request.send(null);
};

In Firefox 1.5 both alerts are display "object".
In Internet Explorer 6 both alerts display "unknown".

It is a quirk of many ActiveX objects that their properties return
"unknown" when tested with - typeof -. That is allowed as they are host
objects and the behaviour of - typeof - becomes implementation dependent
when used on host objects. It is also not a problem as such objects
return "undefined" when a property does not exist on the object, so
"unknown" is a positive indicator that there is something there.
In Opera 9 and Safari 2 both alerts display "function".

I expected the first alert to display "function" and the
second to display "undefined". Do you know what is happening
here?

The - delete - operator does not have to be successful. that is, it does
not have to result in the removal of property of an object. It will be
unsuccessful if the property referred to has the internal - DontDelete -
attribute (as is true of all the properties of the Activation/Variable
object), and host objects may give that attribute to their properties if
they want to, or just prevent - delete - from working on them by other
means.

The - delete - operator is actually very little used in javascript, as it
is only reliable on javascript objects. It can be used, for example, to
keep a sparse array sparse, or to re-expose a prototype property that had
been masked, but may examples of its use are predicated on the
fundamental misconception that - delete - operates on the object referred
to by a property (destroying it, as in many class-based OO languages)
while it actually acts upon the object which has the indicated property
(attempting to remove that property from the object.

Generally, assigning - null - will have the effect of stooping a property
from referring to an object. Exceptions, such as IE's issues with
assigning non-function values to - onreadystatechange - mentioned above
are rare, and can be worked around by assigning values of the required
type to replace the existing references with something harless.

Richard.
 
M

Michael Winter

Richard Cornford wrote:

[snip]
Unfortunately assigning a non-function to the - onreadystatechange -
property does not work in IE ...

Good grief! I have no idea how I missed that. Granted, I've only ever
done it once, but that's no excuse whatsoever.

Gah!

Mike
 
P

Peter Michaux

Hi Richard,

I will read up on the activation/variable object.

I woke up this morning and thought the delete operator was wrong and
thought about setting onreadystatechange to null instead. Thanks for
pointing out that doesn't work before I even tried.


Richard Cornford wrote:

There are two good candidates for breaking the circle; the "request"
property of the Activation/Variable object refers to the xml http request
object, so could have - null - assigned to it after the callback function
processes the response, and - onreadystatechange - property of the xml
http request object refers to the anonymous function object, and could be
re-assigned. Unfortunately assigning a non-function to the -
onreadystatechange - property does not work in IE (an allowable quirk of
some ActiveX objects), so the value assigned to break the circle would
have to be a function, but that function could be a simple (and
re-useable) dummy that just did not have a scope chain that held any
references to any xml http request obejcts.


If setting the request variable to null works then that appeals most
because it is less complicated than the dummy function (not by much but
still a little less.)

In this example the first alert says "object" and the second alert says
"null" in all browsers I tried.

HTTP.getText = function(url, callback) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200) {
callback(request.responseText);
}
alert(typeof request);
request = null;
alert(request);
}
}
request.open("GET", url, true);
request.send(null);
};

Does this look sufficient? It looks like it to me but this is quite
subtle stuff.

If this is correct should this be submitted as errata to Flanagan?

Thank you,
Peter
 
P

Peter Michaux

If setting the request variable to null works then that appeals most
because it is less complicated than the dummy function (not by much but
still a little less.)

In this example the first alert says "object" and the second alert says
"null" in all browsers I tried.

HTTP.getText = function(url, callback) {
var request = HTTP.newRequest();
request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200) {
callback(request.responseText);
}
alert(typeof request);
request = null;
alert(request);
}
}
request.open("GET", url, true);
request.send(null);
};

Does this look sufficient? It looks like it to me but this is quite
subtle stuff.

I tried the above code with Internet Explorer and watched under the
Task Manager performance tab. It looks like the "request = null" line
works. When I comment out this line the Page File Usage History graph
continually grows. When I uncomment the line the garbage collection
seems to work.

I was using a 10 MB response so that request.responseText was very
large with each request.
If this is correct should this be submitted as errata to Flanagan?

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top