2

I've spent days on this and hit it from every angle I can think of. I'm working on a simple windows 7 gadget. This script will pull JSON data from a remote web server and put it on the page. I'm using jQuery 1.6.2 for the $.getJSON. Script consumes more memory each loop.

var count = 1;

$(document).ready(function () {
    updateView();
});

function updateView(){
    $("#junk").html(count);
    count++;
    $.getJSON( URL + "&callback=?", populateView);
    setTimeout( updateView, 1000 );
}

function populateView(status) {
    $("#debug").html(status.queue.mbleft + " MB Remaining<br>" + status.queue.mb + " MB Total");
}

Any help would be greatly appreciated....Thank you!

EDIT: Add JSON data sample

?({"queue":{"active_lang":"en","paused":true,"session":"39ad74939e89e6408f98998adfbae1e2","restart_req":false,"power_options":true,"slots":[{"status":"Queued","index":0,"eta":"unknown","missing":0,"avg_age":"2d","script":"None","msgid":"","verbosity":"","mb":"8949.88","sizeleft":"976 MB","filename":"TestFile#1","priority":"Normal","cat":"*","mbleft":"975.75","timeleft":"0:00:00","percentage":"89","nzo_id":"-n3c6z","unpackopts":"3","size":"8.7 GB"}],"speed":"0  ","helpuri":"","size":"8.7 GB","uptime":"2d","refresh_rate":"","limit":0,"isverbose":false,"start":0,"version":"0.6.5","new_rel_url":"","diskspacetotal2":"931.51","color_scheme":"gold","diskspacetotal1":"931.51","nt":true,"status":"Paused","last_warning":"","have_warnings":"0","cache_art":"0","sizeleft":"976 MB","finishaction":null,"paused_all":false,"cache_size":"0 B","finish":0,"new_release":"","pause_int":"0","mbleft":"975.75","diskspace1":"668.52","scripts":[],"categories":["*"],"darwin":false,"timeleft":"0:00:00","mb":"8949.88","noofslots":1,"nbDetails":false,"eta":"unknown","quota":"","loadavg":"","cache_max":"0","kbpersec":"0.00","speedlimit":"","webdir":"","queue_details":"0","diskspace2":"668.52"}})

EDIT 2: Stripped code down to this and it still leaks. I think that eliminates traversing the DOM as a contributor.

$(document).ready(function () {
    setInterval(updateView, 1000);
});

function updateView(){
    $.getJSON( URL + "&callback=?", populateView);
}

function populateView(status) {
}

EDIT 3: It's not jQuery. I removed jQuery and did it with straight js. Still leaks.

function init(){
    setInterval(updateView, 1000);
}

function updateView(){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", URL, false);
    xhr.setRequestHeader( "If-Modified-Since", "0");
    xhr.send('');
}

So...if it's not jQuery, not just in IE (Chrome too). What the heck?! Ideas?

Thank you!

T3chDad
  • 307
  • 4
  • 11
  • 1
    can you put a fiddle together on jsfiddle.net ? that'll give you a demonstrable example that we can look at. – Russ Clarke Jul 19 '11 at 18:41
  • Also, how much memory are we talking about here ? – Russ Clarke Jul 19 '11 at 18:48
  • What else is going on with the page? What is that "status" variable? – Pointy Jul 19 '11 at 18:48
  • Just to be clear, is it actually leaking or if you leave it running for a while, does it clear up ? – Russ Clarke Jul 19 '11 at 19:02
  • Can you try the accepted answer in this post and see if you still get the problem ? http://stackoverflow.com/questions/2429056/simple-jquery-ajax-call-leaks-memory-in-internet-explorer – Russ Clarke Jul 19 '11 at 20:30
  • Doubt that will work as JSONP is not using ajax, rather is adding script tags to page to bypass same domain restrictions.... I've got the same problem - have you managed to solve it? At the moment, I'm thinking JSONP polling on pages that stay around is a bad idea :( – Kram Oct 09 '11 at 21:50

3 Answers3

2

Edit 2:

If it's actually taskmanager showing the leak here, then I think the next step is to investigate IE, as I believe that IE is then engine used to host Windows Widgets.

If you can recreate your script in a little html file you can run this tool and have a look if it's IE that's doing it:

http://blogs.msdn.com/b/gpde/archive/2009/08/03/javascript-memory-leak-detector-v2.aspx

Also, are you running IE8 or 9 ?


Edit:

Based on the JSON string in the Op; basically the problem is misleading here. the bit of javascript posted is working perfectly fine.

The Server producing the JSON is the one that's showing a difference in memory usage, I would investigate the website/endpoint that's creating that JSON and seeing what the issue is.


Just had a thought,

$.getJSON is just a shorthand function for jQuery's $.ajax call.

I wonder if it makes a different if you change your code to use $.ajax but specifically add the cache mechanism to it:

$.ajax({
  url: URL + "&callback=?",
  dataType: 'json',
  cache: false,
  success: populateView
});

That might stop it trying to store it in memory perhaps, and depending on your browser, it might be showing more memory because you just haven't had your garbage collected, so to speak.

Russ Clarke
  • 17,511
  • 4
  • 41
  • 45
  • Didn't help...still leaks about 5-10K per second. – T3chDad Jul 19 '11 at 18:53
  • No...the client is what's showing the memory leak. The js memory consumption grows every time it polls the server for JSON. – T3chDad Jul 19 '11 at 19:48
  • The client is displaying it, but isn't it making it's calculation based on the values of queue.mbleft and queue.mb ? – Russ Clarke Jul 19 '11 at 19:51
  • Please ignore the JSON content...it's irrelevant. It has nothing to do with what I'm seeing at the client level. I'm sitting here watching task manager on the client side and the memory usage goes up every time it polls the server. I changed the code as stated in a comment above so that the only thing the script is doing is $.getJSON...nothing else. ...and the memory still leaks. – T3chDad Jul 19 '11 at 19:55
  • Yup, sorry - I think we were getting distracted by the fact that your debug message was talking about memory usage; kind of assumed that's where your figure was coming from. – Russ Clarke Jul 19 '11 at 20:00
  • I'm trying the tool now. IE9 is installed (Win7 x64 SP1). – T3chDad Jul 19 '11 at 20:01
  • Ok, what if you also take out the JSON call and just have the timer looping ? I can't test this directly myself as I don't have IE here presently (posting from my Mac). – Russ Clarke Jul 19 '11 at 20:02
  • I've just tested this in chrome using that Fiddle I posted, and watching the Javascript memory usage there, it's also ramping up continuously, but the Chrome engine does appear to periodically reclaim a bunch of memory. Plot thickens, it looks like it really could be jQuery here. – Russ Clarke Jul 19 '11 at 20:11
  • everything is fine if only the counter/interval is running incrementing the div on the html page. No memory leak just from crossing DOM. Only if I do the getJSON will this occur. – T3chDad Jul 19 '11 at 20:15
  • Yes, I'm getting the same result whether or not I use updateTimer or setInterval. Now, it is ultimately clearing up, so its not a leak per se,as it's not lost to the system. As soon as I kill the script I get all my memory back. But yes, this is a good find! – Russ Clarke Jul 19 '11 at 20:20
  • I don't want it to be a "good find"! :) I was hoping it was just some silly thing I was overlooking. Sure, it clears memory when you close the calling app, but that doesn't do much good if you've got this running on the sidebar for days/weeks on end. ;) So...does this mean I found a bug in jQuery 1.6.2 that I should report? – T3chDad Jul 19 '11 at 20:34
  • I think it's definitely worth reporting on their site, mention that we've recreated it in IE and Chrome. – Russ Clarke Jul 19 '11 at 20:49
2

I have the feeling that the setTimeout function within the updateView is causing this behaviour. To test this you can modify your code to:

$(document).ready(function () {
   setInterval(updateView, 1000);
});

function updateView(){
    $("#junk").html(count);
    count++;
    $.getJSON( URL + "&callback=?", populateView);
}

function populateView(status) {
    $("#debug").html(status.queue.mbleft + " MB Remaining<br>" + status.queue.mb + " MB Total");
}

EDIT: The setInterval function will execute the passed in function over and over every x miliseconds. Here to the docs.

EDIT 2: Another performance loose (Although it might not be critical to the issue) is that you are traversing the DOM every second to find the $('#debug') element. You could store that and pass it in as:

        $(document).ready(function () {
            var debug = $('#debug'); 
            var junk = $('#junk')          ;
            setInterval(function(){updateView(debug, junk)}, 1000);

        });

        function updateView(debug, junk){
           junk.html(count);
            count++;
            $.getJSON( URL + "&callback=?", function(status){populateView(status,debug)});
        }

        function populateView(status) {
            debug.html(status.queue.mbleft + " MB Remaining<br>" + status.queue.mb + " MB Total");
        }

Edit 3: I have changed the code above because I forgot to take in the response from the server. Assuming that queue is a property of the returned JSON then the code should be as above.

Edit 4: This is a very interesting issue. Another approach then. Lets assume then that there is still some client side scripts that are clogging the memory. What could this be? As far as is I understand the only two things left are the setInterval and the $.getJSON function. The $.getJSON function is a simple ajax request wrapper which fires a request and waits for the response from the server. The setInterval function is a bit more peculiar one because it will set up timers, fire functions, etc.

I think if you manage to mimic this on your server or even just refresh this webpage in your browser every second/5 secs you you will be able to see whether it is the client or the server that processes your request.

sTodorov
  • 5,435
  • 5
  • 35
  • 55
  • Didn't help...still leaking about 300K/minute. – T3chDad Jul 19 '11 at 18:58
  • @T3chDad, what if you increase the interval to 5 secs. The reason for that being that if there was memory consumption from the jquery traversing of the DOM every second, it would show up. I will add another edit so see that as well. – sTodorov Jul 19 '11 at 19:10
  • This status.queue data, is that being returned by your specific JSON object ? If so then you're measuring the wrong thing, the memory returned is the memory on the thing that's creating the JSON. Can you possibly share the URL ? – Russ Clarke Jul 19 '11 at 19:22
  • @Russ C, I see the mistake. Thanks I will edit the code. Edit, I was referring to a different mistake, then :) – sTodorov Jul 19 '11 at 19:24
  • Increasing to 5 secs and using your edit2 code above: The memory leak slows to roughly 100K/minute. – T3chDad Jul 19 '11 at 19:28
  • I created a fiddle to test this, http://jsfiddle.net/N9yDH/1/ as I suspected, because I can't see his JSON data, the .queue variable is meaningless. – Russ Clarke Jul 19 '11 at 19:28
  • @RussC, internal URL, you can't reach it. I can post the JSON string if that will help. – T3chDad Jul 19 '11 at 19:29
  • yes please! But if it's an internal URL then the problem isn't that your Widget is leaking memory, the Server producing the JSON is. Unless it's the same box ? – Russ Clarke Jul 19 '11 at 19:30
  • Internal URL as in my internal network. It's not local. JSON source is returned by another web server on the same network. I will update the OP with JSON Data. – T3chDad Jul 19 '11 at 19:35
  • 1
    I would say test this because I think RussC has an excellent point. You can either set up some small test in your server code and call this url every second or just clean up the code that you are already using to only make requests to the server (not insert any html/manipulate DOM). Keep monitoring your memory in the mean time. – sTodorov Jul 19 '11 at 19:43
  • Updated my answer based on that JSON. – Russ Clarke Jul 19 '11 at 19:43
  • I commented the lines that manipulate the DOM. So basically, the only thing the script is doing is polling the web server for JSON every second...and the memory is climbing every second as well. No...it's not the server that's showing the memory leak...it's the client. – T3chDad Jul 19 '11 at 19:47
  • Ok, I think we're getting confused here - what are you basing this on, the data displayed in the #debug Tag, or what Taskmanager is showing you ? – Russ Clarke Jul 19 '11 at 19:52
  • @T3chDad, I think it is the server in that case, because there are no client (DOM) manipulations anymore (you have commented them out). You could, however, set up the same test but on the server (poll your JSON service every second). – sTodorov Jul 19 '11 at 19:57
  • @sTodorov, I'm not sure actually, he's basically said that even with no dom manipulation it still ramps up memory in Taskmanager; the mbleft thing from the JSON is a red herring :) – Russ Clarke Jul 19 '11 at 20:01
  • @RussC, I'm basing this solely on TaskManager. The content of the JSON could be anything and this still happens. – T3chDad Jul 19 '11 at 20:13
  • See Edit 4. I was afraid I will run out of characters in the comments :) – sTodorov Jul 19 '11 at 20:15
  • Have a look at this http://jsfiddle.net/N9yDH/5/ - You'll see that it eats up memory whilst doing nothing with the data. – Russ Clarke Jul 19 '11 at 20:21
0

Found this thread trying to find the underlying reason to this problem because I also had a similar problem recently although my memory would increase about 1Mb per minute... I pretty much isolated it to json parsing. Run the ajax command with type: 'text', and you should see that the memory gets cleaned up.

I found a library json_parse.js which recursively parses the json data using the JS engine (not eval). I manually parse the text data to json in the success callback and this works well.

Itamar
  • 81
  • 1
  • 6