views:

1061

answers:

7

I've got a web page that's using jquery to receive some product information as people are looking at things and then displays the last product images that were seen. This is in a jquery AJAX callback that looks pretty much like this:

if(number_of_things_seen > 10) {
  $('#shots li:last-child').remove();
}

$('<li><img src="' + p.ProductImageSmall + '"></li>').prependTo('#shots');

However, it seems to leak quite a bit of memory. Visually, it does the right thing, but the footprint grows indefinitely.

Safari's DOM inspector shows the DOM is how I would expect it to be, but it seems to maintain references to every image that it has displayed (as seen in this screenshot in case anyone is interested).

I've added

$('#shots li:last-child img').remove();

to the removal statement to no noticable effect.

Is there some magic necessary to let the browser release some of this stuff?

+1  A: 

Can you try changing the src of the last-child and see if that makes a difference? You can then move that element to be the first child

//not tested

var $list=$('#shots>li');
$list.filter(':last-child').children('img')
.attr('src', p.ProductImageSmall)
.parent()
.insertBefore( $list.eq(0) );
redsquare
I like the code this suggestion led to, but the images still don't seem to be going away.
Dustin
Dustin when you say the images dont go away can you clarify? Do you mean it doesnt swap the images?
redsquare
It is odd this as the remove in jquery is optimized to prevent leaks. maybe it is the creation of the xhr objects that are leaking?
redsquare
Before I was displaying images, it leaked much more slowly -- something on the order of a million updates before I saw it above 100MB.
Dustin
Dustin, can you clarify if the changing the image src was not working?
redsquare
Yes, I did almost exactly that code. I initialized a list of 10 elements with a 1px transparent image, and cycled them up like that (setting src and title on each one). Saw the same image growth issue.
Dustin
A: 

how long have you observed this 'growing indefinitely' ? some implementations of garbage collectors don't necessarily give memory back to the OS so quickly if at all. can you distill what you are trying to do into a real simple test (eg, setting src of image over and over) w/o ajax or callbacks ? have you / can you try this w/ the other browsers ?

Scott Evernden
I let it run for several hours and a few thousand products scrolling by. I couldn't see that it ever freed anything.
Dustin
Is that likely to happen in a real world used case?
redsquare
@redsquare Most definitely. This is visualizing the usage of a company's product. I expect them to leave it up all day. The images look very nice, but I may have to pull them if the browser will blow up on busy days.
Dustin
@Scott Safari leaked memory more slowly than firefox, but I didn't actually try to figure out what firefox was doing with the memory. I'm quite sure it's the images themselves, though.
Dustin
A: 

From the Mozilla Developer Center on removeChild():

The removed child node still exists in memory, but is no longer part of the DOM. You may reuse the removed node later in your code, via the oldChild object reference.

This is the key - reuse the removed child, instead of repeatedly making a new one.

// I'm sure there is a nicer way to do it in jQuery
if(number_of_things_seen > 10) {
  var shots = document.getElementById("shots");
  var li    = shots.getElementsByTagName("LI");
  var move  = shots.removeChild(li[li.length-1]);
  move.getElementsByTagName("IMG")[0].src = p.ProductImageSmall;
  shots.insertBefore(move, li[0]);
}
Tomalak
That's pretty much what redsquare had me doing. It works, but it doesn't prevent the leak. :/
Dustin
Does it grow the same if you just move the child while not touching the src?
Tomalak
And - have you actually tried *not* using jQuery for this (e.g. try the JS only approach)? Maybe jQuery itself still is responsible somehow.
Tomalak
A: 

ok I have built a test harness for this, it randomly grabs a new image and uses my replace src code

see http://pastebin.me/4936458820c43

redsquare
Have to say dont like what I see here....
redsquare
I don't see the number of images growing in safari. I suppose that's somewhat good. I think that's +1 the xhr theory. I don't quite understanding who's got the img reference, though.
Dustin
Perhaps I'll try out irc tomorrow and see if anyone can tell me the difference between what you're doing and what I'm doing. Thanks for your help (so far).
Dustin
A: 

Dustin, It seems the insertBefore statement in my code was causing some issues. If you see this futher test case the memory usage stays pretty static even though I am emptying the last-child and inserting a new new dom fragment. Performs better than the last attempt.

http://pastebin.me/4936519fea2cf

redsquare
A: 

Is the footprint really a problem?

I suspect that Safari caches all images as long as you stay on the same site and there's nothing you can do about it. If you're lucky Safari releases not-needed objects after some limit is reached.

The reason for this behaviour is, that Safari thinks it's likely that those images are used a second time, and therefore it caches them.

Georg
+2  A: 

Browsers are notorious for memory leaks. It sounds like the problem occurs when the page is left running for a long time. How about refreshing the page before it runs out of memory?

window.setTimeout("location.reload()",1000*60*60);//refresh in an hour
Rob Colburn
Could be modified, that it only refreshes if the user hasn't done anything for 5 minutes or so.
Georg