48

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.

2
  • 8
    Wait, wat? A JS implementation (or more generally, any implementation of a language where manual memory management is taken out of the programmers' hands) without GC seems pretty worthless to me. And in fact, Google showed me code.google.com/apis/v8/design.html#garb_coll as the very first result. Where did you get the "V8 doesn't do GC" idea? Commented Apr 20, 2011 at 16:40
  • 6
    V8 has an ephemeral and linear garbage collector that stops the world when it sweeps. Implying it has no GC is nonsense. In fact, it's one of the best JS GCs we have. Another great one is in IE9+. Mozilla is going to improve their GC design in the future, I heard, towards V8. Commented Jun 30, 2011 at 13:12

3 Answers 3

67

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:

Sign up to request clarification or add additional context in comments.

10 Comments

Circular references shouldn't pose the slightest problem to an "intelligent" GC. Even the simplest GCs can handle them (refcounting is not a real GC). The hint about event handlers seems correct though.
Is it necessary to null "bigObject" inside of a function scope. Shouldn't GC take care of it once the function is complete?
@jwerre that function is never complete. It's a change listener. That function will only be complete if the event emitter returned by doFoo(foo) get's garbage collected.
This is really good. Raynos said there were various other ways to create problems like this; does anybody have a good guide on other things to look out for?
@Raynos Setting bigObject to null doesn't make any sense in this situation in my opinion. Removing the event handler itself will allow bigObject to be collected and that's what we want. However to only null the bigObject reference will just cause a runtime error on the next handler invocation.
|
24

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');
    });

}());

5 Comments

I, too, tend to learn, understand, and accept things best when I can visualize them somehow; this was a good example that allowed me to do just that - to see the variation in memory as GC occurs. Thanks for sharing.
@dukedave I am not sure if that's a good example. Obvioulsy the local buffer variable cannot get garbage collected before the recursive function call ends, unless you explicitly null it but can this behaviour really considered a leak? When the recursive call will end all the local variables will naturally be eligible for GC wheter you null the buffer or not. No?
@plalx I'd consider it a leak insomuch as the application programmer is concerned (i.e. they didn't realize that buffer would grow). Perhaps I should have been more clear that this was addressing the accepted answer's statement that, "Your main problem is not understanding how closures maintain a reference to scope and context of outer functions"; I'll update my answer to reflect that now.
great answer with simple example!
there's no leak, it's a recursive call. a very inteligent compiler could be smarter than you and just remove buffer usage since you're doing nothing with it :)
9

active garbage collection with:

node --expose-gc test.js

and use with:

global.gc();

Happy Coding :)

1 Comment

Manually calling the garbage collector will not help with a real memory leak. The garbage collector is called periodically by the runtime anyway and a memory leak in a GCed language is caused by creating references that the garbage collector cannot safely collect. However when debugging, calling the gc frequently can greatly increase the signal to noise ratio and make it much easier to tell if you have a real memory leak.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.