How does setTimeout() create a memory leak in this code? - javascript

I was reviewing the slides in this presentation: http://slid.es/gruizdevilla/memory
and on one of the slides, this code is presented with the suggestion that it creates a memory leak:
var buggyObject = {
callAgain: function() {
var ref = this;
var val = setTimeout(function() {
ref.callAgain();
}, 1000);
}
}
buggyObject.callAgain();
buggyObject = null;
Can someone explain the issue in more detail here? I might be missing some subtleties here.

This is definitely a memory leak. However, the memory consumption is so small and cannot be measured. I did some small changes to the source code.
I put the entire code inside a loop to create the same scenario 100,000 times
I increased the timer interval to about 16 minutes. This prevents browser from crashing
Here is the code:
for (var i = 0; i < 100000; i++) {
var buggyObject = {
callAgain: function() {
var ref = this;
var val = setTimeout(function() {
ref.callAgain();
}, 1000000); //16m
}
}
buggyObject.callAgain();
buggyObject = null;
}
My experiment:
I ran the code in Chrome Version 34.0.1847.116 m and captured memory changes using Developer Tools\Timeline.
As we can see in the picture, around 32 MB of memory has been consumed for running this code and after a while it's been decreased to about 30 MB and stayed unchanged (see #1).
After several garbage collecting attempts by Chrome (see #2) and one manual forced garbage collection by me (see #3, #4), the memory consumption remains unchanged.
There is no more buggyObject and there is nothing we can do to release the memory. The only possible way is to close the browser.
What causes this?
The main reason for this behavior is the timer. Timer callback and its tied object, buggyObject will not be released until the timeout happens. In our case timer resets itself and runs forever and therefore its memory space will never be collected even if there is no reference to the original object.

There is another question that describes how setTimeout() looks like it has memory leaks, but in reality does not.
However, I think what the author is trying to say is that since buggyObject creates a setTimeout which calls itself, even if you change buggyObject to equal null (saying you are done with the object and it can be cleaned up), the object won't be garbage collected because there is still a reference to it in the setTimeout(). This is technically a memory leak because there is no longer any direct reference to the setTimeout function so that you could clear it later (kind of a zombie timeout if you will).

As advncd pointed out, the timer gets executed and adds even more data on the stack. There is a conceptual view of what happens:
var a = 123;
// call the setTimeout.function
var a = 123;
// call the setTimeout.function
var a = 123;
// call the setTimeout.function
var a = 123;
// call the setTimeout.function
var a = 123;
// call the setTimeout.function
var a = 123;
// call the setTimeout.function
var a = 123;
// call the setTimeout.function
var a = 123;
// call the setTimeout.function
var a = 123;
...etc...
So each time a new variable a is allocated on the stack which grows forever.
What advncd did not mention, though, is the fact that you have a setInterval() function to do what you need to do: have the same function called over and over again. Now you still have a "memory leak" but only the initialization parameters leak (i.e. it won't grow each time the timer times out.)
So conceptually, the calls are flat and you avoid the leak:
a = 123;
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
...etc...

Related

javascript garbage collector not collecting setTimeout()s?

I have a function:
function test()
{
for( var i = 0; i < 1000000; i++ )
{
setTimeout( function()
{
//
}, 10000 );
}
}
Running this on chrome, it propels memory usage from around 50MB to 600MB, which I guess is ok; but after the timeouts have been executed, the garbage collector doesn't seem to remove them from memory, and it just stays at 600MB until I refresh, even then though it leaves some sort of "foot print" of 150MB after the page refresh.
Any idea how to tell the garbage collector to get rid of them after their execution?
You are correct that there appears to be memory that is never cleaned up. My best guess at the problem is that creating a function in a for loop like this creates a new scope which must have access to i. Therefore, those functions never get cleaned. I misspoke - the functions should definitely get cleaned up. Testing in my browser took the memory up over 900MB.
It's important to note that there is no benefit to doing what you are doing and it can be classified as "poor code" at best. You should create one function and reuse it:
// reusing a function fixes the problem
function test () {
var fn = function () {};
for (var i = 0; i < 1000000; i++) {
setTimeout(fn, 1000);
}
}
My observation was that memory usage shot back up over 900MB and then gradually fell back down to closer to normal over the course of a couple minutes.
If you are needing access to the variable i inside of your functions, I hate to say that your code will not give you that. You will only ever see the last value of i (1000000). If you are wanting to use i inside your functions, you can use a factory function. In my tests the memory eventually got cleaned up:
// using a factory fixes the problem and give you access to 'i'
function test () {
function factory (n) {
return function () {
/* This will get called later. 'n' will represent
the value of 'i' at the time this function was created */
}
}
for (var i = 0; i < 1000000; i++) {
setTimeout(factory(i), 1000);
}
};
Unfortunately, the memory problem persists if you use bind:
// Memory problem still exists with .bind
function test () {
var fn = function (n) { };
for (var i = 0; i < 1000000; i++) {
setTimeout(fn.bind(null, i), 1000);
}
}
As for the memory usage remaining high after a page refresh, this I cannot explain but it might have to do with the browser setting aside more memory for a tab which appears to be using a lot of memory. I dunno, just a guess.

javascript: delete reference to the running code

I was just wondering if there is any risk when you execute code like this:
window.doSomething = function() {
window.doSomething = null;
// do some stuff here
}
Will this always run fine, or might there be a situation in which the garbage collector will clean it up while it's still running?
window.doSomething = null ;
That will just remove the property doSomething from window which was previously referencing your function.
The function that you're currently in will run until the end, because entering the function increases the reference count, preventing it from being destroyed prematurely.
After the function is done, it will be scheduled for garbage collection.

Is there memory leak when doing recursively setTimeout invoke within a Dojo class?

We have created an application using Dojo with an clock on the UI. But sometimes the application UI just hung-up there and the clock just stopped. Guessing the JS engine just stopped because the clock is driven by javascript code.
Not sure following code causes memory leak and then causes the hang-up issue. We are using recursively setTimeout invoke to implement the clock.
dojo.declare("xxx.xxx.HomepageHeader", [dijit._Widget, dijit._Templated],
{
widgetsInTemplate: true,
_time :'',
dateUtil: null,
// ....
// ....
prefix :function (value, p)
{
return (value < 10) ? p + value : value;
},
updateTime :function ()
{
var d = new Date();
var _this = this;
var t = [_this.prefix(d.getHours(), '0'), _this.prefix(d.getMinutes(), '0'), _this.prefix(d.getSeconds(), '0')].join(':');
_this._time.innerHTML = t;
_this.dateInfo.innerHTML = this.dateUtil.format(d, "yyyy/MM/dd") + " |&nbsp " + this.dateUtil.format(d, "EEE");
window.setTimeout( function(){_this.updateTime();}, 100);
}
// ....
// ....
}
Noticed that within the class, the method updateTime used window.setTimeout to recursively invoke itself to update time text on the UI.
Is there any memory leak issue here? If the answer is no, is there any possible issue caused the hang up issue?
Thanks!
This isn't really recursion because setTimeout() schedules the next call to updateTime() for some time in the future and then the current updateTime() actually completes. So, there is no build-up of the stack frame and thus it's not really recursion. Also, I don't see any reason for a memory leak here.
The scheme you have should be OK and it used often, but you may want to do it less often than every 100ms just to leave more CPU cycles for other things going on the browser.
If you see the clock stop, it is because the JS engine is stuck or looping, but it's probably stuck in code somewhere else other than the actual clock code.

JavaScript Garbage Collection Seems to Not be Running in Chrome Extension background.html

I am coding a Chrome extension.
I have a $.post() command that runs on a timer in the background (setInterval).
The callback invokes a parse function:
function parseData(new_data) {
new_data = $.parseJSON( new_data );
for(var x=0; x<new_data.length; x++) {
var obj = new CustomObj( new_data[x] );
// I commented out code here in order to help isolate the problem.
}
}
CustomObj is prototyped in a typical JS manner...
function CustomObj(data){
this.data = data;
}
CustomObj.prototype.getName = function() {
return this.data.name;
}
// Of course, there are a few more methods here...
The problem:
The extension will cause major lag. If I set it to do the $.post() every 10s (simply to speed up the appearance of the problem), within 5 minutes refreshing any tab in Chrome will show "Waiting on [My Extension]" for about 30s. Eventually the browser will more or less lock up.
What I've discovered: if I comment out the innards of the for() loop, everything is just peachy. No lag ever. If I simply put the above line in the for(); loop (creating the CustomObj), the problem returns.
It seems like a garbage collection problem, as far as I can tell. I've tried implicitly defining the obj variable as well as explicitly deleting it (though Deleting Objects in JavaScript makes me believe that delete is insufficient ). Nothing seems to work.
Thanks.

How many timers in a window? [duplicate]

I have to use atleast 2 setTimeouts and 1 setInterval. Does this have any dependency on the browser or javascript engine being used?
tl;dr: Don't worry about the cost of timers until you're creating 100K's of them.
I just did a quick test of timer performance by creating this test file (creates 100K timers over and over):
<script>
var n = 0; // Counter used to verify all timers fire
function makeTimers() {
var start = Date.now();
for (var i = 0; i < 100000; i++, n++) {
setTimeout(hello, 5000);
}
console.log('Timers made in', Date.now() - start, 'msecs');
}
function hello() {
if (--n == 0) {
console.log('All timers fired');
makeTimers(); // Do it again!
}
}
setTimeout(makeTimers, 10000); // Wait a bit before starting test
</script>
I opened this file in Google Chrome (v54) on my circa ~2014 Macbook Pro, and went to the Timeline tab in Developer Tools and recorded the memory profile as the page loaded and ran thru 3-4 cycles of the test.
Observations
The timer creation loop takes 200ms. The page heap size starts at 3.5MB pre-test, and levels out at 3.9MB.
Conclusion
Each timer takes ~.002 msecs to set up, and adds about 35 bytes to the JS heap.
On a page you can have as many setTimeouts/setIntervals running at once as you wish, however in order to control each individually you will need to assign them to a variable.
var interval_1 = setInterval("callFunc1();",2000);
var interval_2 = setInterval("callFunc2();",1000);
clearInterval(interval_1);
The same code above applies to setTimeout, simply replacing the wording.
As Kevin has stated, JavaScript is indeed single threaded, so while you can have multiple timers ticking at once, only one can fire at any one time - i.e. if you have one that fires a function which 'halts' in execution, for example with an alert box, then that JS must be 'resumed' before another can trigger I believe.
One further example is given below. While the markup is not valid, it shows how timeouts work.
<html>
<body>
<script type="text/javascript">
function addThing(){
var newEle = document.createElement("div");
newEle.innerHTML = "Timer1 Tick";
document.body.appendChild(newEle);
}
var t1= setInterval("addThing();",1000);
var t2 = setInterval("alert('moo');",2000);
</script>
</body>
</html>
You can use as many as you want. Just remember that JavaScript is single threaded, so none of them can execute in parallel.
var interval_1 = setInterval("callFunc1();",2000); calls eval() which is evil so it's BAD.
Use this instead var interval_1 = setInterval(callFunc1,2000);
And for the question, you may use as many as you want but if all have the same interval between two actions, you better do it this way
var interval = setInterval(function() {
// function1
fct1();
// function2
fct2();
},2000);

Categories

Resources