Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading Node.js Heap Snapshots (created via Nodetime) -- why are my objects not GC'd?

Tags:

node.js

I'm having some surprising results in my Nodetime heap snapshot:

enter image description here

Am I reading this right that there are 1706 instances of a "user" in my heap? This seems absurdly high, I'm not even quite sure how I would have allocated that many. In any case, are there any tips / tricks / hints to find out why these are hanging around? I've already used JSHint on my code to ensure there are no free globals being allocated. Everything should be wrapped in the closure scope of the request... so why aren't the users, posts, etc. being garbage collected when the request finishes? Here's some (redacted) code to show how I'm doing things...

What's very surprising is that I took the above heap snapshot about 10m after the last API call finished. So, those objects were all hanging around for long after the requests which triggered them to be allocated was finished!

code:

var User = require('./user').User,
    Q = require('q');

// this function is called via express' router, eg when the client visits myapi.com/users/zane
function getUser(req, res, next)
{
   var user = extend({},User).initialize();

   Q.ncall(user.model.find, user.model, {'username': req.arguments[0]})
   .then(function(data){
       res.writeHead(200, {});
       res.end(JSON.stringify(data));
   })
   .fail(next).end();
}

And the "User" module looks something like this:

exports.User = extend({}, {
    initialize: function() {
        var Schema = api.mongoose.Schema;
        this.schema = new Schema({
            'username': {'type':String, 'index':true}
        });
        this.model = api.db.model('users', this.schema);
    }

    // ... some other helper functions in here
});

Based upon my express code, above, I'd expect the lifespan of the user object which is allocated to be only as long as the request takes to return. Am I missing some key Node.js GC idea here?

like image 582
Zane Claes Avatar asked Nov 04 '22 14:11

Zane Claes


1 Answers

This line looks suspiciously inefficient:

var user = extend({},User).initialize();

I'm assuming the extend call copies the User object and then calls its initialize method. Why are you copying the User object on each API invocation?

Then in the initialize call you create a new Mongoose schema object and then register it as a model via the api.db.model call. Wouldn't it be better to create the schema once and register it during initialization?

The combination of both of these may be resulting in more objects being created on each call than necessary, and I bet those registered Mongoose models do not GC easily.

like image 134
JohnnyHK Avatar answered Nov 11 '22 05:11

JohnnyHK