Jonathan
Jonathan

Reputation: 9151

Do we manually need to clean up unreferenced variables in a closure?

I'm reading this article (http://javascript.info/tutorial/memory-leaks#memory-leak-size) about memory leaks which mentions this as a memory leak:

function f() {
    var data = "Large piece of data";

    function inner() {
        return "Foo";
    }

    return inner;
}

JavaScript interpreter has no idea which variables may be required by the inner function, so it keeps everything. In every outer LexicalEnvironment. I hope, newer interpreters try to optimize it, but not sure about their success.

The article suggests we need to manually set data = null before we return the inner function.

Does this hold true today? Or is this article outdated? (If it's outdated, can someone point me to a resource about current pitfalls)

Upvotes: 6

Views: 1179

Answers (2)

user663031
user663031

Reputation:

Modern engines would not maintain unused variables in the outer scope.

Therefore, it doesn't matter if you set data = null before returning the inner function, because the inner function does not depend on ("close over") data.

If the inner function did depend on data--perhaps it returns it--then setting data = null is certainly not what you want to, because then, well, it would be null instead of having its original value!

Assuming the inner function does depend on data, then yes, as long as inner is being pointed to (referred to by) something, then the value of data will have to be kept around. But, that's what you are saying you want! How can you have something available without having it be available?

Remember that at some point the variable which holds the return value of f() will itself go out of scope. At that point, at least until f() is called again, data will be garbage collected.

The general rule is that you don't need to worry about memory and leaks with JavaScript. That's the whole point of GC. The garbage collector does an excellent job of identifying what is needed and what is not needed, and keeping the former and garbage collecting the latter.

You may want to consider the following example:

function foo() {
  var x = 1;
  return function() { debugger; return 1; };
}

function bar() {
  var x = 1;
  return function() { debugger; return x; };
}

foo()();
bar()();

And examine its execution in Chrome devtools variable window. When the debugger stops in the inner function of foo, note that x is not present as a local variable or as a closure. For all practical purposes, it does not exist.

When the debugger stops in the inner function of bar, we see the variable x, because it had to be preserved so as to be accessible in order to be returned.

Does this hold true today? Or is this article outdated?

No, it doesn't, and yes, it is. The article is four years old, which is a lifetime in the web world. I have no way to know if jQuery still is subject to leaks, but I'd be surprised if it were, and if so, there's an easy enough way to avoid them--don't use jQuery. The leaks the article's author mentions related to DOM loops and event handlers are not present in modern browsers, by which I mean IE10 (more likely IE9) and above. I'd suggest finding a more up-to-date reference if you really want to understand about memory leaks. Actually, I'd suggest you mainly stop worrying about memory leaks. They occur only in very specialized situations. It's hard to find much on the topic on the web these days for that precise reason. Here's one article I found: http://point.davidglasser.net/2013/06/27/surprising-javascript-memory-leak.html.

Upvotes: 5

Andrew
Andrew

Reputation: 14526

Just in addition to to @torazaburo's excellent answer, it is worth pointing out that the examples in that tutorial are not leaks. A leak what happens when a program deletes a reference to something but does not release the memory it consumes.

The last time I remember that JS developers had to really worry about genuine leaks was when Internet Explorer (6 and 7 I think) used separate memory management for the DOM and for JS. Because of this, it was possible to bind an onclick event to a button, destroy the button, and still have the event handler stranded in memory -- forever (or until the browser crashed or was closed by the user). You couldn't trigger the handler or release it after the fact. It just sat on the stack, taking up room. So if you had a long-lived webapp or a webpage that created and destroyed a lot of DOM elements, you had to be super diligent to always unbind events before destroying them.

I've also run into a few annoying leaks in iOS but these were all bugs and were (eventually) patched by Apple.

That said, a good developer needs to keep resource management in mind when writing code. Consider these two constructors:

function F() {
    var data = "One megabyte of data";

    this.inner = new function () {
        return data;
    }
}

var G = function () {};
G.prototype.data = "One megabyte of data";
G.prototype.inner = function () {
    return this.data;
};

If you were to create a thousand instances of F, the browser would have to allocate an extra gigabyte of memory for all those copies of the huge string. And every time you deleted an instance, you might get some onscreen jankiness when the GC eventually recovered that ram. On the other hand, if you made a thousand instances of G, the huge string would be created once and reused by every instance. That is a huge performance boost.

But the advantage of F is that the huge string is essentially private. No other code outside of the constructor would be able to access that string directly. Because of that, each instance of F could mutate that string as much as it wanted and you'd never have to worry about causing problems for other instances.

On the other hand, the huge string in G is out there for anyone to change. Other instances could change it, and any code that shares the same scope as G could too.

So in this case, there is a trade-off between resource use and security.

Upvotes: 1

Related Questions