JavaScript中的垃圾回收是如何工作的?它与.NET的垃圾回收类似吗?人们是否避免使用VBScript,而选择了JavaScript作为他们的标准客户端语言,是因为VBScript中的垃圾回收实现较差?
JavaScript中的垃圾回收是如何工作的?它与.NET的垃圾回收类似吗?人们是否避免使用VBScript,而选择了JavaScript作为他们的标准客户端语言,是因为VBScript中的垃圾回收实现较差?
垃圾回收是如何工作的?
简短回答是:当一块内存(比如一个对象)不再被引用时,它就可以被回收。具体何时、如何以及是否回收完全取决于实现方式,不同的实现方式也不同。但在语言层面上,这是自动完成的。
例如:
function foo() {
var bar;
bar = new ReallyMassiveObject();
bar.someCall();
}
当foo
返回时,因为没有任何引用指向它,所以bar
指向的对象会自动被垃圾回收。
与下面的情况形成对比:
function foo() {
var bar;
bar = new ReallyMassiveObject();
bar.someCall();
return bar;
}
// elsewhere
var b = foo();
现在对对象的引用在函数调用后仍然存在,并且持久存在,直到/除非调用者将其他内容分配给b
或b
超出范围为止。
与之形成对比:
function foo() {
var bar;
bar = new ReallyMassiveObject();
bar.someCall();
setTimeout(function() {
alert("Three seconds have passed");
}, 3000);
}
即使在foo
返回后,定时器机制仍然引用了定时器回调函数,而定时器回调函数(一个闭包)引用了创建它的上下文,该上下文还包含bar
变量。因此,理论上,当foo
返回时,bar
所引用的内容并不会立即被垃圾回收。相反,在定时器触发并释放对回调的引用之前,它将被保留,使得回调和它所引用的上下文都可以进行垃圾收集。 (实际上,现代JavaScript引擎可以并且确实优化其中的闭包。例如,在上述情况中,静态分析显示回调不引用bar
,也不包含任何可能在运行时动态引用它的eval
或new Function
代码,因此JavaScript引擎可以安全地将bar
排除在函数所引用的上下文之外,从而使其所引用的内容可以进行垃圾回收 - 现代引擎也确实这样做)。关于闭包的更多信息,请参见此文章。
顺便说一下,JavaScript没有处理清除循环引用的问题,因此例如:
function foo() {
var a, b;
a = {};
b = {};
b.refa = a;
a.refb = b;
}
当foo
返回时,a
和b
互相引用并不是问题。由于没有其他东西引用它们两个,它们都可以被清理掉。但是在IE中,如果其中一个对象是主机提供的对象(例如DOM元素或通过new ActiveXObject
创建的对象),而不是JavaScript对象,则情况就不同了。(因此,例如,如果您在DOM元素上放置JavaScript对象引用,并且JavaScript对象引用回DOM元素,则即使没有人引用它们两个,它们也会相互保留在内存中。)但这是IE的一个问题,而不是JavaScript的问题。