鉴于如下所示的典型结构,垃圾收集器何时释放了不同的变量?:
'Use strict';
var $ = require('jquery');
var somePrivateVar = new Whatever();
module.exports = functions (){
var someInsideVar = new Whatother();
var someOtherInsideVar = $('.myStuf');
$(window).scroll(function(){
somePrivateVar.MoreStuff();
doSomeStuff(someInsideVar);
someOtherInsideVar.toggle();
});
};
编辑:提出的另一个问题是相关的,但不是这个问题的重点。我已经对垃圾收集了解了一些。我对处理垃圾或避免垃圾不感兴趣。我对nodejs如何在幕后关闭方式安装模块感兴趣。换句话说,如果你愿意,nodejs如何在对另一个问题的第一个响应中实现这些原则,以有效地处理内存。
答案 0 :(得分:1)
node.js中的一个模块只是一个闭包,它通过与Javascript中的其他闭包保持活动相同的规则保持活跃状态。只要闭包中的任何代码仍然可以被其他代码访问,那么闭包本身就不能被垃圾收集。并且,模块也由模块加载器缓存,这意味着模块的引用在模块缓存中保持活动,即使没有其他代码保留对模块的引用。您可能会发现阅读本文很有帮助:How require() Actually Works因为作用域的正常Javascript GC可以确定何时可以对给定模块进行垃圾回收。模块没有特殊的垃圾收集。
因此,只要在加载模块时创建的模块闭包保持引用,模块变量就会处于活动状态。虽然该闭包是活动的,但是在加载模块时释放模块中引用变量的内容的唯一方法是手动清除这些变量的内容(例如设置为null
)。
默认情况下,节点模块保持活动状态并加载到节点模块缓存中(等待其他代码再次require()
),即使它当前不再被其他人使用或引用码。如果您愿意,可以从缓存中手动删除它。有关从缓存中手动删除模块(以及可能还加载的任何模块)的详细信息,请参阅此答案:Unloading node code/modules。因此,如果您的代码都没有对模块的引用,则模块中没有实时事件处理程序或回调,并且您已从缓存中手动删除模块,那么模块闭包应该不再具有任何可访问的代码引用它并且GC可以释放整个范围(以及模块)。
只要其中的任何代码仍可访问(例如仍可由任何其他代码调用),模块仍然存活。在您的情况下,如果任何其他代码仍然具有对模块的引用或者只要模块中的事件处理程序仍处于活动状态(仍然可以调用),则会出现这种情况,因为事件处理程序回调引用了模块中的代码。并不总是清楚地知道给定的垃圾收集器在知道给定的事件处理程序何时完成以及将来永远不会再次调用时的智能程度。
在您的具体示例中,$(window).scroll()
事件处理程序(这似乎是一个组成示例,因为在node.js中通常没有window
对象进行滚动)理论上永远存在,直到您使用.off()
手动删除事件处理程序或直到window
对象本身被删除或类似的东西。因此,该事件处理程序中的引用永远不会自行消失。
当ajax调用本身已完成执行并且已调用所有回调时,将执行具有特定生命周期的其他事件处理程序,例如Ajax成功处理程序。这些事件处理程序将在完成后释放它们所持有的任何引用。 setTimeout()
也是如此。它会在执行时(或取消定时器时)释放它所持有的任何引用。
通常很难预测垃圾收集器的智能程度,以及何时会意识到给定的变量不再可达,因此可以进行垃圾收集。有些事情很容易理解,例如当一个变量超出范围并且没有其他引用留在范围内时,整个范围将被GCed。但有些事情并非如此简单,例如当一个范围仍然存在时,因为该范围内的事件处理程序仍然存在,但该特定事件处理程序中的任何内容都无法实际引用该范围内的给定变量。在这种情况下,GC是否实际尝试GC在该范围内的单个变量是依赖于实现的。像eval()
这样的事情以及通过字符串操作构建代码来构造新的Function
对象使得Javascript很难确切知道将来从给定的事件处理程序或回调中引用什么和不能引用的内容(因为如果没有解释器知道你将来可能会参考什么,那么可以以编程方式构建几乎任何参考。这使细粒度垃圾收集变得复杂。您可以依赖的是整个范围的垃圾收集(当整个范围发布时)。依靠更细粒度的GC而不是它可能不明智。如果你明确地使用范围内的一个非常大的变量来完成可能会持续很长时间,那么只有null
这个非常大的变量更安全,所以当大数据的特定引用被清除时你希望它被清除。这对于一个小字符串来说并不重要(除非你有成千上万的这些对象),但可能与大缓冲区或非常大的字符串相关。
编辑:看起来V8会对范围内的各个变量进行垃圾收集,如果这些变量本身未在闭包内仍可访问的任何代码中引用且没有用途eval()
在同一代码中。我没有找到关于这个主题的任何权威参考资料,但已经证实在测试实际情况时似乎是这种情况。