Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory leaks and closures in JavaScript - when and why?

You quite often read on the web that using closures is a massive source of memory leaks in JavaScript. Most of the times these articles refer to mixing script code and DOM events, where the script points to the DOM and vice-versa.

I understand that closures can be a problem there.

But what about Node.js? Here, we naturally don't have a DOM - so there is no chance to have memory leaking side effects as in browsers.

What other problems may there be with closures? Can anybody elaborate or point me to a good tutorial on this?

Please note that this question explicitly targets Node.js, and not the browser.

like image 976
Golo Roden Avatar asked Oct 23 '13 19:10

Golo Roden


People also ask

What causes memory leak in JavaScript?

The main cause of memory leaks in an application is due to unwanted references. The garbage collector finds the memory that is no longer in use by the program and releases it back to the operating system for further allocation.

How do we avoid memory leaks in closures?

Only capture variables as unowned when you can be sure they will be in memory whenever the closure is run, not just because you don't want to work with an optional self . This will help you prevent memory leaks in Swift closures, leading to better app performance.

Can you leak memory using JavaScript?

Memory leaks can and do happen in garbage collected languages such as JavaScript. These can go unnoticed for some time, and eventually they will wreak havoc. For this reason, memory profiling tools are essential for finding memory leaks.


2 Answers

You can find a good example and explanation in this blog post by David Glasser.

Well, here it is (I added a few comments):

var theThing = null; var cnt = 0; // helps us to differentiate the leaked objects in the debugger var replaceThing = function () {     var originalThing = theThing;     var unused = function () {         if (originalThing) // originalThing is used in the closure and hence ends up in the lexical environment shared by all closures in that scope             console.log("hi");     };     // originalThing = null; // <- nulling originalThing here tells V8 gc to collect it      theThing = {         longStr: (++cnt) + '_' + (new Array(1000000).join('*')),         someMethod: function () { // if not nulled, original thing is now attached to someMethod -> <function scope> -> Closure             console.log(someMessage);         }     }; }; setInterval(replaceThing, 1000); 

Please try it out with and without nulling originalThing in Chrome Dev Tools (timeline tab, memory view, click record). Note that the example above applies to browser and Node.js environments.

Credit also and especially to Vyacheslav Egorov.

like image 31
borisdiakur Avatar answered Sep 29 '22 14:09

borisdiakur


This question asks about something similar. Basically, the idea is that if you use a closure in a callback, you should "unsubscribe" the callback when you are finished so the GC know that it can't be called again. This makes sense to me; if you have a closure just waiting around to be called, the GC will have a hard time knowing that you're finished with it. By manually removing the closure from the callback mechanism, it becomes unreferenced and available for collection.

Also, Mozilla has published a great article on finding memory leaks in Node.js code. I would assume that if you try out some of their strategies, you could find parts of your code that express leaky behavior. Best practices are nice and all, but I think it's more helpful to understand your program's needs and come up with some personalized best practices based on what you can empirically observe.

Here's a quick excerpt from the Mozilla article:

  • Jimb Esser’s node-mtrace, which uses the GCC mtrace utility to profile heap usage.
  • Dave Pacheco’s node-heap-dump takes a snapshot of the V8 heap and serializes the whole thing out in a huge JSON file. It includes tools to traverse and investigate the resulting snapshot in JavaScript.
  • Danny Coates’s v8-profiler and node-inspector provide Node bindings for the V8 profiler and a Node debugging interface using the WebKit Web Inspector.
  • Felix Gnass’s fork of the same that un-disables the retainers graph
  • Felix Geisendörfer’s Node Memory Leak Tutorial is a short and sweet explanation of how to use the v8-profiler and node-debugger, and is presently the state-of-the-art for most Node.js memory leak debugging.
  • Joyent’s SmartOS platform, which furnishes an arsenal of tools at your disposal for debugging Node.js memory leaks

The answers to this question basically say that you can help the GC out by assigning null to closure variables.

var closureVar = {}; doWork(function callback() {   var data = closureVar.usefulData;   // Do a bunch of work   closureVar = null; }); 

Any variables declared inside a function will go away when the function returns, except those that are used in other closures. In this example, closureVar has to be in memory until callback() is called, but who knows when that will happen? Once the callback has been called, you can give a hint to the GC by setting your closure variable to null.

DISCLAIMER: As you can see from the comments below, there are some SO users who say that this information is out of date and inconsequential for Node.js. I don't have a definitive answer on that yet; I'm just posting what I've found on the web.

like image 55
RustyTheBoyRobot Avatar answered Sep 29 '22 14:09

RustyTheBoyRobot