Skip to main content

Avoiding Memory Leaks:
A peek in Browsers' Closure's Optimization

In this post, I want to cover a Closure optimization meant to help us preventing memory leaks. This optimization is implemented by the three major browsers: Chrome, Firefox and IE. Closure is a popular subject and you can find very good articles out there. Although the post won't cover Closures internals, I will briefly cover the basics in order to keep the reading fluent.

Basics

To put it shortly; any time we access a method in JS, a special context object is created. This object contains the variables to be searched in the scope chain search process.

  
var x = 10;
(function parentFunc () {
    console.log(x);
}()); //* Result: 10

The code above will print 10 because when the parentFunc method is executed, a special context object containing its parent's variables is created and is pointed by parentFunc method scope. When the interpreter starts searching for 'x' value it searches inside the parentFunc scope. If it can not find it, its parent scope gets evaluated. In this case, the parent scope is the global scope (window in browsers implementation, global in nodeJS).

Inner functions actually capture the entire parent scope and add it to the chain:

 
var x = 10;
var funcResult = (function parentFunc () {
    var x = 20;
    var y = 30;
    var sonFunc = function () {
        console.log(x + y);
    }
    return sonFunc;
})();
funcResult();//* Result 50

When sonFunc is returned, the parentFunc scope with x (20) and y (30) variables is captured and added to the search chain. When funcResult is called, and console.log(x + y) is encountered, the interpreter searches the variables 'x' and 'y' inside sonFunc. Once it concludes it can't find them, it searches them inside its parent scope, (as I've explained, parentFunc was added to the chain).

Note that if we comment-out var x = 20 code line, the result will be 40. Because the interpreter won't find the 'x' variable inside parentFunc, it will search in parentFunc's parent scope where 'x' value is 10.

Classic Memory Leak

JS's function-callback nature leads a lot of developers to make use of Closures, but the first thing you should note when using Closures is that they can lead to memory leaks. In order to prevent leaks while using callbacks/closures you must be always aware of what your code is keeping in memory and make sure it vanishes as soon as possible. For example, let's examine the following code snippet:

 
var listFunc= [];

setInterval(function hello(){
var funcResult = (function parentFunc () {
    var x = 20;
    var y = 30;
    var sonFunc = function () {
        var text = "X value is: " + x + " and Y value is: " + y;
        console.log(text);
    }
    return sonFunc;
})();
listFunc.push(funcResult);  

},100);

In the above sample for each interval iteration parameters, 'x' and 'y' are re-created and kept in memory. Newbies might think that sonFunc keeps in memory only the string value but in practice 'x' and 'y' values are kept in memory as well, making this a classic memory leak.

Browsers' Closure's Optimization - Avoid Severe Memory Leaks

In the previous sample, the inner function had a reference to both external values, as result 'x' and 'y' were kept in memory by the internal closure function. In the next example the inner function holds a reference to x variable only:

 



var listFunc= [];

setInterval(function hello(){
var funcResult = (function parentFunc () {
    var x = 20;
    var y = 30;
    var sonFunc = function () {
        var text = "X value is: " + x;
        console.log(text);
    }
    return sonFunc;
})();
listFunc.push(funcResult);  

},100);




ECMAScript specification, in this case, doesn't refer to the preferred behavior to be implemented. The original browsers' behavior was to keep all the variables inside the closure, even those who were not referenced. In the case above, 'y' was kept in memory, creating a memory leak.

The good news is that as of this writing, modern browsers had implemented a very useful optimization and only referenced variables are now kept in memory by the internal method.

eval is evil

Ok, so browsers scan the function's values and keep only the referenced once. But what if the internal function makes use of eval(), would the browser smart enough to detect the referenced values?

 
var listFunc= [];

setInterval(function hello(){
var funcResult = (function parentFunc () {
    var x = 20;
    var y = 30;
    var sonFunc = function () {
        var text = "X value is: " + x;
        eval("alert(some_other_text)");
    }
    return sonFunc;
})();
listFunc.push(funcResult);  

},100);

In this case, it can be very difficult for the browser to detect the referenced variables. As a result if the function makes use of eval() the optimization won't work and all the parent function's variable will be kept in memory by the internal function.

Conclusion

Closures are very useful in Javascript, but using them requires the developers to be very careful. Although browsers are trying to prevent memory leaks they can do it only in certain circumstances. Keep references only to required variables and avoid using eval() inside closures because it will completely prevent browsers' optimization, completely exposing you to memory leaks.

Comments

The Best

GetHashCode Hands-On Session

The following is a hands-on post meant to demonstrate how GetHashCode() and Equals() methods are used by .NET Framework under the hood. For the sake of simplicity I will refer to the popular hashed-base Dictionary type, although any other hash based structure will follow a similar behavior, if not the same one. After understanding this post you should be able to spot potential problematic behaviors and resolve them, prevent creation of unreachable items in dictionaries and improve CRUD actions performance on hash based structures. The TheoryGetHashCode() is used to create a unique integer identifier for objects/structs. The hashcode can be used for two purposes: Programmatically, by developers, to distinguish objects/structs form each other (NOTE: Not recommended when the default .NET implementation is used, as it's not guaranteed to preserve the same hash between .NET versions and platforms)Internally, by .NET Framework, when using the object/struct as a key in a hashed based l…

Closures in C# vs JavaScript -
Same But Different

Closure in a Nutshell Closures are a Software phenomenon which exist in several languages, in which methods declared inside other methods (nested methods), capture variables declared inside the outer methods. This behavior makes captured variables available even after the outer method's scope has vanished.

The following pseudo-code demonstrates the simplest sample:
Main() //* Program starts from here { Closures(); } AgeCalculator() { int myAge = 30; return() => { //* Returns the correct answer although AgeCalculator method Scope should have ordinarily disappear return myAge++; }; } Closures() { Func ageCalculator = AgeCalculator(); //* At this point AgeCalculator scopeid cleared, but the captured values keeps to live Log(ageCalculator()); //* Result: 30 Log(ageCalculator()); //* Result: 31 } JavaScript and C# are two languages that support…

Method Breakpoints are Evil

Some IDEs expose an option to set "Method Breakpoints", it turns out that "Method Breakpoints" might tremendously decrease debugger's performance. In this article, I explain what are "Method Breakpoints" and the reasons they impact performance so badly. To better understand this subject I will cover how Debuggers works under the hoods and how Breakpoints and Method Breakpoints are implemented internally. Java Platform Debugger Architecture JDPA is an architecture designed for enabling communication between debuggers and debugees. The architecture consists of three APIs: JVM Tool Interface (JVM TI) - A native interface which defines the services a VM must provide for debugging purposes Java Debug Wire Protocol (JWDP) - A textual API which defines the communication's protocol between debugee and debuggerJava Debug Interface (JDI) - Defines a high-level Java API designed to facilitate the interaction between debugge and debuggers. Internally J…