Aync XMLHTTP with Javascript: memory problems

J

Justice

Currently I'm doing some experimenting with the XMLHTTP object in
Javascript. Now,
the XMLHttp object is asynchronous (at least in this case), and the
following code causes a significant memory loss even though I seem to be
allocaitng everything; help would be *vastly* appreciated. What am I doing
wrong here? I thought I was doing everything correctly (setting things to
null, for example) but none of the memory seems to get replaced. I've been
experiencing allocations of memory up to 125 megabytes (!) that simply
persist in the browser, never to go away...

Thanks in advance for any help you can provide!

ASyncQueue = new Array();
-Justice
function DataHandlerLookupLoop(value, id, numberOfLoops)

{

for (var loopCount = 0; loopCount < numberOfLoops; loopCount++)

{

DataHandlerLookup(loopCount,loopCount,id);

}

}

function DataHandlerLookup(value, number, id)

{

++asyncsRunning;

AsyncQueue.push(new Object());

AsyncQueue[number].ThisElement = document.getElementById(id);

AsyncQueue[number].Server = new ActiveXObject("Msxml2.XMLHTTP");

AsyncQueue[number].Server.onreadystatechange = f;

AsyncQueue[number].Server.open("GET",
"http://localhost/MyCompany/data.metadata?text=" + value, true);

AsyncQueue[number].Server.send(value);

}



function f()

{

var temporaryLength = AsyncQueue.length;

for (var index = 0; index < AsyncQueue.length; ++index)

{

if (AsyncQueue[index] != null)

{

if (AsyncQueue[index].Server.readyState == FINISHED)

{

AsyncQueue[index].Server.abort();

AsyncQueue[index].Server = null;

delete AsyncQueue[index].Server;

AsyncQueue[index].ThisElement = null;

delete AsyncQueue[index].ThisElement;

AsyncQueue[index] = null;

--asyncsRunning;

}

if (asyncsRunning == 0)

{

var tempQueueLength = AsyncQueue.length;

for (var ix = 0; ix < tempQueueLength; ++ix)

{

AsyncQueue.pop();

}

CollectGarbage();

}

}
 
S

Steve van Dongen

Currently I'm doing some experimenting with the XMLHTTP object in
Javascript. Now,
the XMLHttp object is asynchronous (at least in this case), and the
following code causes a significant memory loss even though I seem to be
allocaitng everything; help would be *vastly* appreciated. What am I doing
wrong here? I thought I was doing everything correctly (setting things to
null, for example) but none of the memory seems to get replaced. I've been
experiencing allocations of memory up to 125 megabytes (!) that simply
persist in the browser, never to go away...
<snip />

Been there, done that.
AsyncQueue[index].Server.abort();

AsyncQueue[index].Server = null;

delete AsyncQueue[index].Server;

AsyncQueue[index].ThisElement = null;

delete AsyncQueue[index].ThisElement;

AsyncQueue[index] = null;
You don't need to call abort() or any of the 4 lines after that.
CollectGarbage();
CollectGarbage won't solve your problems. It is only useful in
debugging as it forces the garbage collector to run at a known point.

Aside from executing code too many times (the f() function is called 3
times per HTTP request and you loop through each item in your array),
there doesn't appear to be any problems noticable at first glance.
But this isn't your actual code, is it? It doesn't make much sense to
make HTTP requests but do nothing with the results....

Regards,
Steve
 
J

Justice

Steve van Dongen said:
Been there, done that.
AsyncQueue[index].Server.abort();

AsyncQueue[index].Server = null;

delete AsyncQueue[index].Server;

AsyncQueue[index].ThisElement = null;

delete AsyncQueue[index].ThisElement;

AsyncQueue[index] = null;
You don't need to call abort() or any of the 4 lines after that.
CollectGarbage();
CollectGarbage won't solve your problems. It is only useful in
debugging as it forces the garbage collector to run at a known point.

I simply threw it in to see if memory was actually being deallocated.
Unfortunately, nothing really changed.
Aside from executing code too many times (the f() function is called 3
times per HTTP request and you loop through each item in your array),

How can I necessarily avoid calling f three times, however? Attaching f to
onreadystatechange basically guarantees that this thing is going to get
called every time.

Ideally, I would have liked a way in "f" to determine which server object
fired the onreadystatechange event, but no information comes back from
onreadystatechange to indicate this, hence my convoluted attempts at
resolving things.
there doesn't appear to be any problems noticable at first glance.
But this isn't your actual code, is it? It doesn't make much sense to
make HTTP requests but do nothing with the results....

You're correct. However, even this code (without anything notable in
function "f", which would do something w/ the results) leads to memory
leaks. If I simulate an environment where, say, 400 of these requests are
made, the memory piles up and piles up and eventually I have IE using 156
megabytes of memory.

Unfortunately, although there don't *seem* to be any problems noticeable
(that's what I thought too, other than some semi-inefficient ways of going
through the asynclist) the memory allocations for this are enormous when 200
or so requests go through.

Thanks for any insight you can provide,
-Justice
 
S

Steve van Dongen [MSFT]

Steve van Dongen said:
Been there, done that.
AsyncQueue[index].Server.abort();

AsyncQueue[index].Server = null;

delete AsyncQueue[index].Server;

AsyncQueue[index].ThisElement = null;

delete AsyncQueue[index].ThisElement;

AsyncQueue[index] = null;
You don't need to call abort() or any of the 4 lines after that.

It looks like you may actually need to set .Server and .ThisElement =
null though frankly I'm not sure why.
I simply threw it in to see if memory was actually being deallocated.
Unfortunately, nothing really changed.


How can I necessarily avoid calling f three times, however? Attaching f to
onreadystatechange basically guarantees that this thing is going to get
called every time.

Ideally, I would have liked a way in "f" to determine which server object
fired the onreadystatechange event, but no information comes back from
onreadystatechange to indicate this, hence my convoluted attempts at
resolving things.

See below.
You're correct. However, even this code (without anything notable in
function "f", which would do something w/ the results) leads to memory
leaks. If I simulate an environment where, say, 400 of these requests are
made, the memory piles up and piles up and eventually I have IE using 156
megabytes of memory.

Unfortunately, although there don't *seem* to be any problems noticeable
(that's what I thought too, other than some semi-inefficient ways of going
through the asynclist) the memory allocations for this are enormous when 200
or so requests go through.

Whenever you use nested functions, a closure is created, giving the
enclosed function access to all of the outer function's arguments and
local variables. This is a common source of memory leaks. The
problem comes down to how relatively easy it is to create circular
references when you don't realize what is really going on. The
JScript garbage collector is capable of breaking circular references
between any number of JScript objects, but it cannot break a circular
reference where the chain includes an external object like an element
in IE or a COM object.

So, the point is basically, try not to mix nested functions and DHTML
and/or ActiveX because you might end up with a circular reference that
cannot be broken and, therefore, a memory leak.

This is an example of a function that leaks memory...
function omicron()
{
var x = MakeNewDiv();
x.foo = epsilon;
function epsilon()
{
...
}
}
The circular reference is:
x --> div --> epsilon --> x

That said, the only way to pass data to your onreadystatechange
handler is to use a closure. This example doesn't leak...

AsyncQueue = new Array();
asyncsRunning = 0;

function Init()
{
for (var i=0; i<500; i++)
{
AsyncRequestStart("", "id" + i);
}
}

function AsyncRequestStart(value, id)
{
var index;
var requestInfo = new Object();

index = AsyncQueue.length;
AsyncQueue.push(requestInfo);

// requestInfo.ThisElement = document.getElementById(id);
requestInfo.Server = new ActiveXObject("Msxml2.XMLHTTP");
requestInfo.Server.onreadystatechange = function () {
AsyncRequestComplete(index);
};
requestInfo.Server.open("GET",
"http://localhost/MyCompany/data.metadata?text=" + value, true);
requestInfo.Server.send(value);
asyncsRunning++;
}

function AsyncRequestComplete(index)
{
var requestInfo = AsyncQueue[index];
Log(index + ": readyState=" + requestInfo.Server.readyState);
if (requestInfo.Server.readyState == 4 /*FINISHED*/)
{
asyncsRunning--;
UpdateStatus();

// requestInfo.ThisElement = null;
requestInfo.Server = null;
delete AsyncQueue[index];
}
requestInfo = null;
}

function Log(s)
{
window.status = s;
}

function UpdateStatus()
{
document.body.innerText = "Pending Requests: "
+ asyncsRunning;
}

Regards,
Steve
 
J

Justice

It looks like you may actually need to set .Server and .ThisElement =
null though frankly I'm not sure why.

I thought I had read something about references to the DOM causing leaks in
other's applications. (Part of it is just my paranoia in Javascriptabout
setting everything to null once I am done with the objects).

[explanation of closures and circular references]
See, I figured that *might* have been the problem and thus rearchitected
things to pull "f" out of the closure. However, that still didn't do
anything in terms of the memory usage. (see below where I reply to your
example)
That said, the only way to pass data to your onreadystatechange
handler is to use a closure. This example doesn't leak...

Thanks for your example! However, running your example through tresting and
looking at IE's memoery usage before and after:


IE before Init() is called:

Physical Memory: 14944 K
Peak Memory Usage: 14,944 K
Virtual Memory: 6,640 K

IE after Init() finishes:
Physical Memory: 23,880 K
Peak Memory Usage: 25,472 K
Virtual Memory: 16,872 K

And, just for kicks, I jacked up your Init function to loop 4,000 times
instead of just 500. Here were my results after it finished:

Physical Memory: 66,830 K
Peak Memory Usage: 92,104 K
Virtual Memory Usage: 73,104 K

This memory usage does not go away until I close IE (it stays even when
navigating to other sites). This is the memory leak I am referring to,
which now you have caught as well!

Therein lies my problem...?

Thanks,

-Justice
 
S

Steve van Dongen [MSFT]

Thanks for your example! However, running your example through tresting and
looking at IE's memoery usage before and after:


IE before Init() is called:

Physical Memory: 14944 K
Peak Memory Usage: 14,944 K
Virtual Memory: 6,640 K

IE after Init() finishes:
Physical Memory: 23,880 K
Peak Memory Usage: 25,472 K
Virtual Memory: 16,872 K

And, just for kicks, I jacked up your Init function to loop 4,000 times
instead of just 500. Here were my results after it finished:

Physical Memory: 66,830 K
Peak Memory Usage: 92,104 K
Virtual Memory Usage: 73,104 K

This memory usage does not go away until I close IE (it stays even when
navigating to other sites). This is the memory leak I am referring to,
which now you have caught as well!

Sounds like you're measuring at different states. Before Init() is
called there are 0 pending requests; when Init() is complete there are
500 (or 4000) pending requests. You have to measure when the pending
request count reaches 0 again.

When I was testing my code I found that when I refreshed the page the
memory usage before and after was pretty consistent. Adding
CollectGarbage() at the end made it a little more consistent which is
what I'd expect. Another thing I noticed was that on the first run
memory usage after all the requests had completed was higher than at
the beginning. I suspect the discrepancy is the result of IE delay
loading some libraries and not indicative of a memory leak caused by
the script.

Regards,
Steve
 

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
474,262
Messages
2,571,045
Members
48,769
Latest member
Clifft

Latest Threads

Top