什么是 JavaScript 垃圾回收?

2022-08-30 00:03:29

什么是 JavaScript 垃圾回收?对于Web程序员来说,为了编写更好的代码,了解JavaScript垃圾回收有什么重要意义?


答案 1

Eric Lippert不久前写了一篇关于这个主题的详细博客文章(另外将其与VBScript进行了比较)。更准确地说,他写了关于JScript的文章,JScript是微软自己实现的ECMAScript,尽管与JavaScript非常相似。我想你可以假设绝大多数的行为对于Internet Explorer的JavaScript引擎都是一样的。当然,实现会因浏览器而异,尽管我怀疑您可以采用一些共同的原则并将其应用于其他浏览器。

引自该页面:

JScript 使用非一代标记和清除垃圾回收器。它的工作原理如下:

  • 每个“在范围内”的变量都被称为“清道夫”。拾荒者可以指一个数字,一个对象,一个字符串,等等。我们维护一个拾荒者列表 - 变量在进入范围时移动到scav列表,当它们超出范围时移出scav列表。

  • 垃圾回收器不时运行。首先,它在每个对象,变量,字符串等上放置一个“标记” - GC跟踪的所有内存。(JScript 在内部使用 VARIANT 数据结构,并且该结构中有很多额外的未使用位,因此我们只设置其中一个。

  • 其次,它清除了清除器上的标记和清除器引用的传递闭包。因此,如果拾荒者对象引用了不可清除对象,那么我们清除了不可清除对象上的位,以及它所引用的所有内容上的位。(我使用“关闭”这个词的含义与我之前的帖子不同。

  • 此时,我们知道仍然标记的所有内存都是分配的内存,任何作用域内变量的任何路径都无法访问该内存。所有这些对象都被指示自行拆除,这会破坏任何循环引用。

垃圾回收的主要目的是让程序员不必担心他们创建和使用的对象的内存管理,尽管当然有时无法避免它 - 至少对垃圾回收的工作原理有一个粗略的想法总是有益的。

历史说明:答案的早期版本对操作员的引用不正确。在 JavaScript 中,delete 运算符从对象中删除属性,这与 C/C++ 完全不同。deletedelete


答案 2

当涉及 DOM 对象时,请注意循环引用:

JavaScript 中的内存泄漏模式

请记住,仅当没有对对象的活动引用时,才能回收内存。这是闭包和事件处理程序的常见陷阱,因为某些JS引擎不会检查内部函数中实际引用了哪些变量,而只是保留封闭函数的所有局部变量。

下面是一个简单的示例:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

一个幼稚的JS实现不能收集,只要事件处理程序在附近。有几种方法可以解决这个问题,例如在(不适用于局部变量和函数参数:从对象中删除属性,并且变量对象无法访问 - 严格模式下的ES5甚至会抛出一个,如果你尝试删除局部变量!)。bigStringbigString = nullinit()deletedeleteReferenceError

我建议,如果您关心内存消耗,请尽可能避免不必要的闭包。