admin管理员组

文章数量:1287913

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

The above example shows a classic, accidental (or maybe not) memory-leaking closure. The V8 garbage collector can't determine if it's safe to remove the bigObject because it's being used in the callback function which can be called several times.

One solution is to set bigObject to null when the job in the callback function is over. But if you are using many variables (imagine there is n variables like bigObject, and they are all used in callback) then cleaning this bees an ugly problem.

My question is this: is there any other way to clean those used variables?

EDIT Here's another (real world) example: So I get application from mongodb and pare it to some other application. Callback from mongodb uses variable application that is defined out of that callback. After I get result from mongodb I return it also as a callback (because it is all async and I cant just write return ). So actually it can happen that i propagate callback all the way to the source...

function pareApplications(application, condition, callback) {

    var model = database.getModel('Application');
    model.find(condition, function (err, applicationFromMongo) {
        var result = (applicationFromMongo.applicationID == application.applicationID)
        callback(result)        
    }
}
function(foo, cb) {
  var bigObject = new BigObject();
  doFoo(foo, function(e) {
     if (e.type === bigObject.type) {
          cb();
          // bigObject = null;
     }
  });
}

The above example shows a classic, accidental (or maybe not) memory-leaking closure. The V8 garbage collector can't determine if it's safe to remove the bigObject because it's being used in the callback function which can be called several times.

One solution is to set bigObject to null when the job in the callback function is over. But if you are using many variables (imagine there is n variables like bigObject, and they are all used in callback) then cleaning this bees an ugly problem.

My question is this: is there any other way to clean those used variables?

EDIT Here's another (real world) example: So I get application from mongodb and pare it to some other application. Callback from mongodb uses variable application that is defined out of that callback. After I get result from mongodb I return it also as a callback (because it is all async and I cant just write return ). So actually it can happen that i propagate callback all the way to the source...

function pareApplications(application, condition, callback) {

    var model = database.getModel('Application');
    model.find(condition, function (err, applicationFromMongo) {
        var result = (applicationFromMongo.applicationID == application.applicationID)
        callback(result)        
    }
}
Share Improve this question edited Mar 30, 2016 at 10:28 ᴍᴇʜᴏᴠ 5,2764 gold badges45 silver badges60 bronze badges asked May 8, 2013 at 13:36 Ivan LonginIvan Longin 3,3534 gold badges36 silver badges43 bronze badges 11
  • Let me ask you this - why is this a problem? The change handler is meant to be called several times. So how will you (or the GC) ever know when it's really the end of using bigObject unless you unbind the change event? You seem to want one instance of bigObject so that the handler can pare types. You instantiate it once, which reduces the load for every time the handler runs. If you want it to be cleaned up, instantiate it inside the handler every time, or expect it to "leak" memory because that's how it works. – Ian Commented May 8, 2013 at 13:47
  • How about using .one() instead of .on()? – frenchie Commented May 8, 2013 at 13:59
  • 1 Please notice that I don't use jQuery and I don't bind or register to some event. I just pass callback function to another. I use NODE.JS ! – Ivan Longin Commented May 8, 2013 at 14:25
  • 1 What concerne me is that I can potentialy have chain of callbacks...for example business function calls data function which calls mongodb and all munication between these is in callbacks. Maybe callback from mongodb realy clean everything but what with these other callbacks in business and data layers. I suppose that GC only see callback and what is underneath him he doesn't look. I haven't yet run analysis for leaks but I can clearly see that my node.js workers are using too much rss(RAM) memory. – Ivan Longin Commented May 8, 2013 at 15:27
  • 1 Yes but in every that kind of callback (in business or data layer) you can have a closure and are you really sure that they will clean up? ... listen to this example. In some other part of code I also have reading from database but in stream. I have stream.on(data){...} event and every time when I collect 1000 records I fire up callback for business layer (in my example in first post it will be this one : callback(result)) ... that implementation of callback function in business layer is actually a closure and it works perfectly fine. Why didn't GC cleaned up that closure after first callback? – Ivan Longin Commented May 8, 2013 at 20:12
 |  Show 6 more ments

2 Answers 2

Reset to default 2

If your callback function is only supposed to be called once, then you should unsubscribe after it is called. That will release your callback + closure to the GC. With your closure released, bigObject will also be free to be collected by the GC.

That's the best solution - as you noted, the GC doesn't magically know your callback will only be called once.

To build on Brandon's answer: If (for some terrible reason) you are unable to unsubscribe your callback you could always handle deleting the callback yourself:

function createSingleUseCallback(callback)
{
    function callbackWrapper()
    {
        var ret = callback.apply(this, arguments);
        delete callback;
        return ret;
    }
    return callbackWrapper;
}

function pareApplications(application, condition, callback)
{
    var model = database.getModel('Application');
    model.find(condition, createSingleUseCallback(function (err, applicationFromMongo)
    {
        var result = (applicationFromMongo.applicationID == application.applicationID);
        callback(result);
    })
}

本文标签: nodejsClosure and callback memory leak in javascriptStack Overflow