11

I've spent the whole weekend to elaborate the memory growth issue within my web application written in Dojo.

The web application is going to "run forever", so restarting the browser is not planned.

The application is updating dynamic data from server (no cross domain) on a regular basis; each 5 seconds, an AJAX call is made to retrieve the new data as JSON.

By letting the application run for hours, I observed a constant growth in browser's memory (both on latest Chrome and Firefox, both on latest Windows and Mac OS X).

At first, I thought Dojo was causing this behaviour. And indeed, by switching to native implementation with the XMLHttpRequest object I could reduce the memory growth dramatically, but it still exists. With each AJAX request the memory is growing a little bit (about 4-8KB).

What I've already tried:

I have...

  • ...tried to use other frameworks such as jQuery, YUI, etc. - no effect
  • ...switched to using the native `XMLHttpRequest` object - helped a lot, but not entirely
  • ...deactivated DOM manipulation after retrieved data - no effect
  • ...resetted `xhr` by setting to `null` and deleting it after each iteration - no effect
  • ...resetted onreadystatechange handler to null or an empty method after each iteration - no effect
  • ...reused the `xhr` object and the `onreadystatechange` handler as it's always the same - no effect

So even if I do nothing (as also described in the first StackOverflow link below) with the loaded data, the memory usage increases:

What I've already read:

My test HTML code:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Native XHR Async</title>
</head>
<body>
<script>

var update = document.createElement("div");
document.body.appendChild(update);

var timeout = null;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = onReadyState;

function loadData()
{
    xhr.open("GET","memory-leak.json?" + new Date().getTime());
    xhr.send(null);
}

function onReadyState()
{
   if (this.readyState === 4)
   {
       if( this.status === 200 )
       {
           update.innerHTML = "";
           update.innerHTML = this.responseText;
       }

       if( timeout !== null )
       {
           clearTimeout(timeout);
           delete timeout;
       }

       timeout = setTimeout(loadData, 1000);
   }       
}

loadData();

</script>

</body>
</html>

And my test JSON (in the same directory):

{
    "errorstatus":"Okay",
    "B0":{"P":"0 Watt"},
    "E0":{"P":"28 Watt"},
    "Z0":{"P":"28 Watt"},
    "S0":{"P":"0 Watt","SOC":"74%"},
    "Z1":{"P":"0 Watt"},
    "R0":0,
    "R1":0,
    "R2":0,
    "date":"29.09.2012 09:23:19",
    "Version":"Sep 28 2012-15.22"
}

I don't have any explanation for this issue, so any help is much appreciated. If you need any further information about this, please don't hesitate to ask me.

Community
  • 1
  • 1
arm1n
  • 151
  • 1
  • 7
  • It seems quite normal that a page uses more memory as it is open longer. What you **should** worry about is if the browser doesn't get back the memory when the page is closed. – user2428118 Oct 01 '12 at 15:01
  • Thank you for your response! The problem disappears when the browser tab is closed and reopened again. But as I said, that's not an option, as the app should run permanently - besides, also a page refresh doesn't empty the memory ... And I really don't understand why browsers don't get rid of these old ajax requests, even if I try to "prepare" them for garbage collecting? – arm1n Oct 01 '12 at 16:25
  • Did you find a satisfying solution eventually? Facing the exact same issue. – Albert Apr 10 '13 at 09:06
  • I'm having the same issue on Safari (Mac) as well. Our display also runs for ever and I use XMLHttpRequest to talk to a node.js server periodically. If you do find a solution, let us know :-) – Mayoneez May 17 '13 at 07:20
  • Unfortunately the only solution in our case was to reload the page after a given period of time. So we set the interval to to refresh the page once per day. – arm1n May 26 '13 at 13:29

1 Answers1

2

The XmlHttpRequest will cache each response which is why you're appending the date to make the URL unique.

The cache is written to disk as normal, but also will be held in an XmlHttpRequest which has made a request as long as it exists.

You should use an instance per request and destroy it, or otherwise make sure caching is disabled via pragmas and xhr settings.

Rob Hardy
  • 1,821
  • 15
  • 15
  • Hi Rob, thank you for your response! Unfortunately, neither using a new instance per request and destroying it each time, nor the pragma and xhr settings approach to prevent caching (adapted from [here](http://ajaxpatterns.org/XMLHttpRequest_Call#How_will_caching_be_controlled.3F)) brought the desired effect. Memory usage is still growing by time and leads finally to a browser crash! – arm1n Oct 13 '12 at 12:27
  • 1
    Why are you calling `abort()` in your callback code? When you call abort the state of the xhr changes to 0 (uninitialized) - see http://msdn.microsoft.com/en-us/library/ie/ms535920(v=vs.85).aspx. It's very possible that this behaviour is preventing cleanup of the xhr contents because the underlying system thinks that the request was never made. I'd say don't use the abort method in normal operation anyway as this 'cancels' the request you're making. When the hxr goes out of scope it will clean itself up effectively. Only use it to cancel a request in progress. Let me know if this helps. – Rob Hardy Oct 14 '12 at 15:30
  • Hi Rob! You're right, it's not necessary to call `abort()` in this case. The actual code uses the method only, if the request was timed out. Unfortunately, the memory still grows, even if I don't use the `abort()` method. But thanks again for your help! – arm1n Oct 17 '12 at 09:08