I am reading this article on javascript optimization. Article
I came across this section and it tells me when a leak occurs. But I can't find what is the proper way to call it so a leak does not occur. Here is the section I am interested in.
One of the worst places to leak is in a loop, or in setTimeout()/setInterval(), but this is quite common. Consider the following example.
var myObj = {
callMeMaybe: function () {
var myRef = this;
var val = setTimeout(function () {
console.log('Time is running out!');
myRef.callMeMaybe();
}, 1000);
}
};
If we then run:
myObj.callMeMaybe();
to begin the timer, we can see every second "Time is running out!" If we then run:
myObj = null;
The timer will still fire. myObj won’t be garbage collected as the closure passed to setTimeout has to be kept alive in order to be executed. In turn, it holds references to myObj as it captures myRef. This would be the same if we’d passed the closure to any other function, keeping references to it.
It is also worth keeping in mind that references inside a setTimeout/setInterval call, such as functions, will need to execute and complete before they can be garbage collected.
The question is: How do you do this properly so that you don't leak? Is it as simple as calling clearInterval? And does this leak once or leak once per interval
1 Answer 1
I wouldn't call this a memory leak in any fashion - it's simple garbage collection doing what it's supposed to. There is no more "proper" way to do it.
The object that was initially pointed to by myObj is still in use as long as your timer is running. A garbage collector will free it as soon as there are no more references to it. Setting myObj = null clears one reference to it, but your ongoing timers have another reference in myRef so it can not be garbage collected until ALL references to it are gone.
Yes, if you stop your timer and set myObj = null, then there will be no more references to the object and the GC will get rid of it. Keep in mind that you will need to provide access to the timerid if you want to stop the timer from the outside because no outside code can get to val where you have it stored now.
If you had lots of other data in myObj that the timer did not need access to and you were trying to allow that data to be freed while the timer continues to run, then you can either keep the same structure you have now, but clear that other data from the object (either remove the properties or set the properties to null) or you can change the structure of your code so that the recurring timer is launched with a separate function call and doesn't have to keep a reference to the object in order to call a method.
In other words, if your timer method needs access to the object, then the object is correctly kept alive by the garbage collector. If the timer method doesn't need access to the object, then you should run the recurring timer some other way that doesn't repeatedly call a method on the object - allowing the object to be garbage collected.
5 Comments
setTimeout won't mess with the one that's already set. You'd have to call clearTimeout. Alternatively, you could check whether myObj is equal to myRef, and simply not call setTimeout again in that case. (You should have access to myObj via the closure.) But note that if you do it that way, then if you want an additional timeout, you'd have to store it in a different variable.setTimeout again). So if that's the only thing holding a reference to it, it'll become garbage.setTimeout() finishes as long as you don't call setTimeout() again, creating a new reference. You don't have to call clearTimeout() after setTimeout() has fired.
valis not available outside functions, so you need some factory to manage all timers in application. To call clearInterval you need know the value ofval