最近我遇到了一个node.js API的问题,我的内存随着每个请求而变得越来越大。我正在Heroku上使用他们的免费版本托管我的服务器,该版本只有512MB的RAM。在周末获得大量流量之后,我开始从Heroku开始超出内存错误,所以我开始在我的代码中搜索内存泄漏无济于事。我没有留下任何物品,一切都应该得到清理,坦率地说,我迷路了。
然而,在做了一些研究之后,我发现node.js在达到max-old-space-size变量时运行垃圾收集器,在64位系统上默认为1024MB。我把它设置为410(我可用内存的80%)但是想知道我是否应该在代码中处理这个问题?显然,升级我的实例并且只有正常的默认上限是理想的,但现在不是一个选项。
示例:
// lets assume there is some apiGet function
// that calls back with a very very large object with
// the following structure:
// {
// status: "success",
// statusCode: 200,
// messages: [],
// data: { users: [ huge array of users ] }
// }
// we have a manipulateData function that's going
// to do some stuff to the returned data and the
// call some callback to give the data to the caller
function manipulateData(callback) {
apiGet(function(error, veryLargeObject) {
var users = veryLargeObject.data.users;
var usefulUsers = users.map(function(user) {
// do some data calculations here and then
// return just those properties we needed
});
callback(null, usefulUsers)
});
}
所以在这个例子中,一旦完成了manipulateData的运行,如果我理解正确,现在将设置“veyLargeObject”进行垃圾收集,因为没有更多的指针可以访问它(返回的有用的用户是由地图创建的新数组)。但这并不一定意味着它占用的所有内存都是免费的,对吗?在调用回调之前设置veryLargeObject = null或undefined是否明智?
我希望我所要求的是有道理的。基本上:当没有意图再使用它们时,将大对象设置为null或undefined是一个好主意还是应该留下来让垃圾收集器清理?当你只有512MB的RAM而不是8GB的RAM时,这个问题的答案会改变吗?
答案 0 :(得分:2)
如果您确定不再需要某个给定对象,那么将其设置为null是要走的路(请注意,这并不意味着任何链接对象也会被垃圾收集)。只有当对该给定对象的所有引用都设置为null(对象变得无法从代码中的任何位置访问)时,才会收集它。
由于node.js使用了引擎盖下的V8引擎,因此您可以获得有关如何改进垃圾收集A tour of V8: Garbage Collection的一些提示。如果这还不够,您可以按照these instructions强制执行GC。
答案 1 :(得分:1)
只有在某种闭合情况下才需要将变量设置为null
,例如:
function createClosure(bigData) {
var unrelatedVar = 1;
doSomethingAsync(function theCallback(err, result) {
if (bigData.matches(result)) {
...
}
});
return function theReturnedFunction() {
return unrelatedVar++;
};
}
在V8中,相同级别的闭包共享相同的上下文对象,其中关闭了变量。所有相同级别的闭包然后指向上下文对象,因此它将保持活动直到所有函数都死亡。所以这里theReturnedFunction
和theCallback
都是同级函数,它们都指向具有2个成员的相同上下文对象:bigData和unrelatedVar。所以很久
因为返回的函数是活动的,所以bigData也是活着的,即使它不能被引用。
这很容易陷入,因为封闭的变量看起来与局部变量完全相同,而实际上它们就像一个对象的字段(它会使用显式的this.field
所以它总是如此明显)。这与在未使用之后将显式对象的.bigData
字段设置为null无关,但当它是一个明确的对象时,它更难以对未命中。