最近在使用node做遊戲服務器架構,其異步回調機制對處理大批量請求非常有效,而且避免了多線程造成的理解困難,死鎖等一系列問題。
服務器在外網放了兩個星期,一次都沒掛過,確實很穩定。
但好事不長,遊戲上線之前開始做壓力測試,當併發數量達到500個時,內存直接達到70%以上,cpu達到100%,導致程序直接崩潰。
node不是有垃圾回收機制嗎?怎麼還會產生內存泄漏的問題。
後來經過仔細調查,發現是閉包造成的內存泄漏。因爲閉包保留了對其作用域內的東西的引用,而這正是通常的內存泄露之源。
好了,舉個例子。
//第一種情況,變量在外層函數,內層函數引用外層函數的變量。內層函數並沒有被全局變量引用,只是執行。
function aa(c)
{
var ads={};//變量在回調函數外部
for(var i=0;i<20000000;i++)
{
ads[i]=i;
}
c(ads);
}
function bb()
{
var num=200;
aa(function(arr){
console.log(ads[1]);
})
}
bb();
setInterval(function(){},1000);//使用定時器保持進程一直在執行狀態
在Linux上測試發現,內存使用率達到10%以上,並且一直存在不會釋放。當執行bb函數是,其內部的函數傳入了匿名函數,形成了函數內部存在函數的機制,即閉包。該匿名函數引用了外部函數的變量ads。
//第二種情況,變量在回調函數(內層函數)內部。
function aa(c)
{
c();
}
function bb()
{
aa(function(){
var ads={};//變量在回調函數內部
for(var i=0;i<20000000;i++)
{
ads[i]=i;
}
})
}
bb();
setInterval(function(){},1000);//使用定時器保持進程一直在執行狀態
同樣,在Linux上測試發現,內存使用率達到10%以上,並且一直存在不會釋放。
//第三種情況,變量在外層函數,但內部函數並不執行。
function aa(c)
{
var ads={};//變量在回調函數內部部
for(var i=0;i<20000000;i++)
{
ads[i]=i;
}
}
function bb()
{
aa(function(){
})
}
bb();
setInterval(function(){},1000);//使用定時器保持進程一直在執行狀態
同樣,在Linux上測試發現,內存使用率達到10%以上,並且一直存在不會釋放。
最後得出結論,當函數裏面存在內部函數,則形成閉包機制,活動環境中產生的一切變量都會保存在內存中不會釋放。不管該內部函數是否被外部變量引用。