PDA

View Full Version : setTimeout, closures, and IE memory leak help



ddadmin
03-10-2007, 07:55 AM
Hi:
I'm trying to understand how to resolve an IE memory leak that occurs in the following general situation. Basically, if I use a closure inside setTimeout() to call an object instance method repeatedly, in which the method accesses the .innerHTML property of an element to modify it's HTML, a leak occurs in IE. For example:


<div id="numberdiv">holder</div>

<script type="text/javascript">
function timemachine(offset){
this.offset=offset
this.display()
}
timemachine.prototype.display=function(){
//Use the innerHTML property to assign rich HTML to an element, in combo with closure- leak in IE!
document.getElementById("numberdiv").innerHTML='<p><b>'+this.offset+new Date().getSeconds()+'</b></p>'
var _this=this
//Closure below, no leak by itself
setTimeout(function(){_this.display()}, 1000)
}
var sample=new timemachine(5)
</script>


Apparently it's the closure coupled with the rich HTML assignment that creates the leak. I've tried a few standard techniques to plugging the leak, but nothing has worked. Anyone know of an elegant solution that doesn't require "re-wiring" everything? Any pointers appreciated.

jscheuer1
03-10-2007, 08:07 AM
They always tell you to create another closure to avoid the IE memory leak. However, I've been running your code for awhile now and memory seems constant (physical, kernel (paged and nonpaged)). That's with IE 7 though.

ddadmin
03-10-2007, 08:16 AM
I'm testing this in IE6, though I've seen cases of leaks even in IE7, which includes the above example.


They always tell you to create another closure to avoid the IE memory leak.

I'd love an example of this. :) I've tried the double closure route, though I must have gotten lost in my own code, because it didn't do anything in halting the perpetual memory increase.

jscheuer1
03-10-2007, 08:30 AM
I'll have to have a look at this again later as I am winding down for now but, since I don't see the leak here, it would be hard to test. Any particular type of memory that you see as beng most affected? Also, just a thought, try switching to interval:


<div id="numberdiv">holder</div>

<script type="text/javascript">
function timemachine(offset){
this.offset=offset
this.display()
}
timemachine.prototype.display=function(){
//Use the innerHTML property to assign rich HTML to an element, in combo with closure- leak in IE!
document.getElementById("numberdiv").innerHTML='<p><b>'+this.offset+new Date().getSeconds()+'</b></p>'
var _this=this
//Closure below, no leak by itself
if(!this.counting)
this.counting=setInterval(function(){_this.display()}, 1000)
}
var sample=new timemachine(5)
</script>

Twey
03-10-2007, 08:59 AM
Anyone know of an elegant solution that doesn't require "re-wiring" everything?It's already inelegant by the use of innerHTML. Does the leak still occur using DOM methods?

ddadmin
03-10-2007, 10:44 AM
Thanks guys. I haven't tried switching over to DOM methods yet (versus innerHTML), mainly because I need the script to run ASAP, without having to wait for the DOM or document to load fully first. Also, this serves as a good chance for me to clarify my understanding of how leaks occur in IE.

Twey
03-10-2007, 10:50 AM
So long as the parent element is loaded, the DOM usually doesn't mind if the page isn't finished yet.

jscheuer1
03-10-2007, 03:15 PM
It is my opinion that the vagaries of the IE memory leak are such that one can only understand it in so far as it either is a problem with a particular bit of code or it is not. That being said there are principles to follow when one is encountered.

I'm willing to play with this code to see what might be done to avoid a leak when using it. To do so I would need to know what type or types of memory you see are being affected by it. In taskmanager there are several indicators of memory usage:


PF Usage


Physical Memory (K)

Total

Available

System Cache

Kernel Memory (K)

Total

Paged

Nonpaged


Which of these indicators are you seeing the problem with or are you using another utility to measure the leak?

There are other considerations:


Does simply running the code cause the leak?

Does reloading the page cause the leaking and/or more leaking?

Does unloading the page restore the memory or must one close the browser?


Notes: I think Twey's idea of using the DOM is worth a shot but, it usually isn't one of the methods mentioned for resolving these leaks. That doesn't mean that it will not do the trick in this case.

Adding a closure could be as simple as:


function timemachine(offset){
this.offset=offset
this.display()
}
timemachine.prototype.display=function(){
var _this=this;
(function(){
//Use the innerHTML property to assign rich HTML to an element, in combo with closure- leak in IE!
document.getElementById("numberdiv").innerHTML='<p><b>'+_this.offset+new Date().getSeconds()+'</b></p>'
})();
//Closure below, no leak by itself
setTimeout(function(){_this.display()}, 1000)
}
var sample=new timemachine(5)

ddadmin
03-10-2007, 09:46 PM
Thanks John, you've given me more to go by. Here are the important points surrounding my test:

1) I'm using Drip (v0.5) (http://www.outofhanwell.com/ieleak/index.php?title=Main_Page) to do the main testing. I should mention that the browser on my machine is IE7, though I'm pretty sure Drip either uses IE6, or some rigged up version of IE that's not the stock IE7.

2) I gave your double closure example a quick run, though same result in Drip- that is, memory leaks still occur.

3) To exaggerate the problem for testing purposes, I changed the setTimeout delay in my original code from 1000 to 100. Here are some screen captures:

http://www.hostreach.com/dynamicindex6/leak.gif

http://www.hostreach.com/dynamicindex6/leak2.gif

http://www.hostreach.com/dynamicindex6/leak3.gif

It's clear memory is being sucked up, at least in Drip, with the problem squarely on the rich HTML that's being assigned to the element not being discarded each time it's reset:


getElementById("numberdiv").innerHTML='<p><b>'+_this.offset+new Date().getSeconds()+'</b></p>'

The more elements I add (P, B, DIV etc), the more leaks (not text nodes). I've tried using the DOM to dynamically remove the nodes one by one before reassigning new content to it using innerHTML (based on this article (http://javascript.crockford.com/memory/leak.html)), though that didn't seem to do anything either. Something like:


while (getElementById("numberdiv").hasChildNodes())
getElementById("numberdiv").removeNode(...)
getElementById("numberdiv").innerHTML='<p><b>'+_this.offset+new Date().getSeconds()+'</b></p>'

4) Regarding memory usage when the page is unloaded, nope, the memory isn't reclaimed, unless Drip is closed then relaunched.

5) I agree too that Trey's idea of switching to using the DOM entirely, instead of .innerHTML, may very well fix the problem, but I'm as curious as I'm interested in finding a way to fix the problem when it comes to using .innerHTML in such a manner.

jscheuer1
03-11-2007, 07:48 AM
I really think that this a bug in Drip. I loaded your original code in IE 6 on a spare machine with taskmanager running and just let it go for several minutes. None of the memory indicators I mention before showed any leakage. I'll leave it up there for awhile. If I see any memory problems, I will come back to this thread to report them but, nothing so far.

Twey
03-11-2007, 12:21 PM
John, have you tried it with Drip? It may be due to differing setups, rather than different programs.

jscheuer1
03-11-2007, 04:20 PM
John, have you tried it with Drip? It may be due to differing setups, rather than different programs.

I suppose but, what set up would that be? I used a genuine copy of IE 6 running as the primary browser on Windows XP home edition and the native taskmanager utility that comes with that OS. I had virtually no other programs running (just anti-virus, LAN stuff and Windows Explorer, that sort of thing). I was a little skeptical so I even changed the timeout from 1000 to 100 to accelerate anything that might be happening and let it run for at least a half hour. There were no significant changes in memory or CPU use. CPU use stayed at 1 or 2 percent.

All of this is in stark contrast to Drip which showed a rapidly ascending line of increased memory use even with the 1000 millisecond timeout value.

jscheuer1
03-11-2007, 10:02 PM
I've looked into this further as, after rereading Twey's remarks, I thought I may have missed something. So, I loaded Drip on the same machine that was showing no problems and it showed none there except when I hit Show Dom Usage. Then it spiked and kept rising. As soon as I closed that, Drip fell back to baseline. It looks to me that Drip is, in some cases, just showing its memory usage, not the memory required by the page.

Now, on my newer, more frequently used machine, Drip showed the ever increasing demand for memory simply by loading the page into it. However, when the same page is loaded into IE, taskmanager still shows no leak, even on the newer machine.

So, I still think it is Drip, not the page that has the problem.

Have you tried analyzing the memory use of the page in the browser via taskmanager without Drip running at all?

ddadmin
03-12-2007, 12:50 AM
Thanks John for all that research, I must say the last thing I suspected was Drip itself being the problem. You said on one machine, hitting "show dom" seem to set Drip off. I don't see that with Drip 0.5 on mine- that is to say, it goes off pretty consistently whenever there's a page in which Drip says there are leaks (by hitting "Show Dom leaks").

I think another interesting question, regardless of whether my sample script sucks up memory in IE, is how to modify the example script I cited so hitting "Show DOM leaks" no longer shows any leaks. As mentioned, before setting the .innerHTML property, I tried dynamically removing any nodes within that element first, among a few other things (short of switching completely over to DOM methods to replace .innerHTML). So far I've yet to find a way around not getting Drip to report no leaks, putting aside the memory issue for now.

jscheuer1
03-12-2007, 03:27 AM
Well, the recommendation for stopping leaks on or linked to the Drip page is to remove innerHTML in favor of removing nodes. So go figure.

Here is what I saw:

Case One -
On Win XP Media Edition with IE 7 as the primary IE:
I load the page into Drip 5.0 and immediately, without hitting any buttons there is a climb in memory use shown on the graph. However, when I load the page into IE 7 on that machine and track memory usage with taskmanager alone (Drip not running), there is no drain on memory in any of the categories I mentioned before.

Case Two -
On Win XP Home Edition with IE 6 as the primary IE:
Drip shows no problems simply running the page. The Show Dom Leaks view reports no leaks, is blank and has no effect on the memory graph. If I hit Show Dom Usage, the graph starts to climb but only one of each of the div p and b are shown. Closing that returns the graph to normal (base line). Closing Drip and using IE 6 and taskmanager to track memory shows no problem with the page.

Now, getting back to your desire:


how to modify the example script I cited so hitting "Show DOM leaks" no longer shows any leaks [in Drip]

I think the way to do that is to fix the bug(s) in Drip, not to rewrite the script.

Added:

Found on quirksmode.org


Drip is indeed not perfect; sometimes it shows references that are actually nicely automatically cleaned by the browser and on the other hand it fails to show memory leaks due to closures.

ddadmin
03-12-2007, 07:10 AM
Great, thanks again John for the insight. I'll probably start taking the feedback from Drip with a grain of salt now, though it still seems to be the best "automated" tool, if not the only one out there, for detecting memory leaks in IE.

jscheuer1
03-12-2007, 07:19 AM
I think Drip is a very good tool but, no tool should be taken as an authority in place of actual test results for the application and OS involved without good reason.

Let's say you had detected a leak in taskmanager, then Drip becomes a good posibility as a diagostic tool. However, unless there is a leak to begin with, there is little reason to even load the page into Drip in the first place.

ddadmin
03-12-2007, 08:00 AM
Well, being the lazy person that I am, it's much easier to load up a page in Drip then stare at the screen for a couple of minutes observing some slight ascending of a graph on the screen. :)