syuichi-tsuji
9/8/2014 - 1:41 AM

起きる!メモリーリーク(javascriptクロージャー編)

起きる!メモリーリーク(javascriptクロージャー編)

わかりやすい事例としてsetTimeoutで実行してみる

var leak = function(){
    var x = 0;
    process.on('exit', function(){
        x++;
    });
    return function(){
        return x;
    }
}
var update = function(){
    var h = leak();
    setTimeout(update, 0);
}
update();

node.jsのEventEmitterはデフォルトで10件までしか登録できないようになっているので それを超えるとワーニングが出る。

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace
    at process.addListener (events.js:160:15)
    at process.on.process.addListener (node.js:773:26)
    at leak (leak.js:9:13)
    at update [as _onTimeout] (leak.js:17:13)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

メモリリークは起きる

var hoge = function(){
    var x = 0;
    process.on('exit', function(){
        x++;
    });
    return function(){
        return x;
    }
}
var h = hoge();
console.log(h());
console.log(h());
console.log(h());

依存しているhの参照が切れた段階ではxはガベージコレクタの対象にならない
xがnode.jsが動いている限りずっと破棄されないprocessオブジェクトから参照され続けるため。

クロージャーの中でイベントハンドラを登録するときは慎重に。

メモリリークは起きない

var hoge = function(){
    var x = 0;
    return function(){
        return x++;
    }
}
var h = hoge();
console.log(h());
console.log(h());
console.log(h());

依存しているhの参照が切れた段階でxはガベージコレクタの対象になる