How to prevent memory leaks in node.js?


Question

We know node.js provides us with great power but with great power comes great responsibility.

As far as I know the V8 engine doesn't do any garbage collection. So what are the most common mistakes we should avoid to ensure that no memory is leaking from my node server.

EDIT: Sorry for my ignorance, V8 does have a powerful garbage collector.

1
45
5/21/2011 12:24:58 PM

Accepted Answer

As far as I know the V8 engine doesn't do any garbage collection.

V8 has a powerful and intelligent garbage collector in build.

Your main problem is not understanding how closures maintain a reference to scope and context of outer functions. This means there are various ways you can create circular references or otherwise create variables that just do not get cleaned up.

This is because your code is ambigious and the compiler can not tell if it is safe to garbage collect it.

A way to force the GC to pick up data is to null your variables.

function(foo, cb) {
    var bigObject = new BigObject();
    doFoo(foo).on("change", function(e) {
         if (e.type === bigObject.type) {
              cb();
              // bigObject = null;
         }
    });
}

How does v8 know whether it is safe to garbage collect big object when it's in an event handler? It doesn't so you need to tell it it's no longer used by setting the variable to null.

Various articles to read:

65
4/20/2011 4:48:37 PM

I wanted to convince myself of the accepted answer, specifically:

not understanding how closures maintain a reference to scope and context of outer functions.

So I wrote the following code to demonstrate how variables can fail to be cleaned up, which people may find of interest.

If you have watch -n 0.2 'ps -o rss $(pgrep node)' running in another terminal you can watch the leak occurring. Note how commenting in either the buffer = null or using nextTick will allow the process to complete:

(function () {
    "use strict";

    var fs = require('fs'),
        iterations = 0,

        work = function (callback) {
            var buffer = '',
                i;

            console.log('Work ' + iterations);

            for (i = 0; i < 50; i += 1) {
                buffer += fs.readFileSync('/usr/share/dict/words');
            }

            iterations += 1;
            if (iterations < 100) {
                // buffer = null;

                // process.nextTick(function () {
                    work(callback);
                // });
            } else {
                callback();
            }
        };

    work(function () {
        console.log('Done');
    });

}());

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon