How timer intervals can leak memory.

Thirty-seven years ago, I published my first article, titled Debugging Memory Allocation Errors. The article was about problems in C and C++. You would think that in a garbage-collected language such as JavaScript, those concerns would be in the distant and unpleasant past.

Well here we are, nearly four decades later, and the latest vexing problem at work is memory leaks in JavaScript. It turns out that garbage collection only gives different, more-subtle opportunities for mistakes.


The setInterval function is a common example. I’m sure you can easily see why the following snippet would quickly consume all your memory.

var ary = [];

setInterval(function() {
  var longString = new Array(1000000).join('*');
  ary.push(longString)
}, 1000);

More subtle is what happens here:

function myInnocentFunction() {
  var longString = new Array(1000000).join('*');

  setInterval(function() {
    if (longString.length < 100)
      console.log("It's not long anymore!");
  }, 1000);

  // Pretend more code is here, which executes
  // until the function finally returns.
}

myInnocentFunction();

You might think that after myInnocentFunction has returned, all its local variables would be freed. That’s not the case. The timer interval is still running, and it owns a reference to longString, meaning that the garbage collector will not free the string’s memory. If you’re in a modern, single-page application, this will continue until you close the browser tab, which is a real problem.

The solution is to definitively clear the timer when you’re done with it:

function myInnocentFunction() {
  var timerNumber;
  var longString = new Array(1000000).join('*');
  
  try {
    timerNumber = setInterval(function() {
      if (longString.length < 100)
        console.log("It's not long anymore!");
    }, 1000);

    // Pretend more code is here, which executes
    // until the function finally returns.
  } finally {
    clearInterval(timerNumber);
  }
}

myInnocentFunction();

This time, we’ve called clearInterval on the timer.

What about setTimeout? Once its interval has elapsed, does it release its references? For example, in the following code, do you think the call to clearTimeout matters?

function myInnocentFunction() {
  var timerNumber;
  var longString = new Array(10000000).join('*');
  
  try {
    timerNumber = setTimeout(function() {
      if (longString.length < 100)
        console.log("It's not long anymore!");
    }, 1000);
  } finally {
    clearTimeout(timerNumber);
  }
}

myInnocentFunction();

The answer is no; the memory for longString will be released even without the call to clearTimeout. Note, however, that the reference inside the setTimeout does prevent longString from being freed until that function is executed. In the example above longString will hang around for at least 1000 milliseconds…unless you call clearTimeout first.

Another reason to call clearTimeout is that there is a limit to the number of timers. The limit is huge (MAXINT+1) but in some cases that may be important.

Finally, what if your intervals and timers are wrapped in a library such as AngularJS? Such libraries may do some favors for you, but they are not miracle-workers. Angular, for example, does caution that you must “explicitly destroy” the intervals that its $interval service creates.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s