【问题标题】:What is JavaScript garbage collection?什么是 JavaScript 垃圾回收?
【发布时间】:2010-10-26 06:10:39
【问题描述】:

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

【问题讨论】:

标签: javascript garbage-collection


【解决方案1】:

Eric Lippert 不久前就这个主题写了一个detailed blog post(另外将它与 VBScript 进行比较)。更准确地说,他写了关于JScript,这是微软自己的ECMAScript 实现,虽然与JavaScript 非常相似。我想您可以假设 Internet Explorer 的 JavaScript 引擎的绝大多数行为都是相同的。当然,实现会因浏览器而异,但我怀疑您可以采用一些通用原则并将它们应用于其他浏览器。

引自该页面:

JScript 使用非分代 标记和清除垃圾收集器。它 像这样工作:

  • “范围内”的每个变量 被称为“清道夫”。清道夫 可以指一个数字、一个对象、一个 字符串,随便。我们维护一个列表 清道夫——变量被移动 当他们来的时候进入 scav 名单 进入范围并退出 scav 列表时 他们超出了范围。

  • 时不时的垃圾 收集器运行。首先它放了一个 在每个对象、变量上“标记”, 字符串等 - 跟踪的所有内存 由 GC。 (JScript 使用 VARIANT 内部和那里的数据结构 有很多额外的未使用的位 那个结构,所以我们只设置一个 他们。)

  • 其次,它清除了 清道夫和传递闭包 清道夫参考。所以如果一个 scavenger 对象引用 a nonscavenger 对象然后我们清除 非清道夫上的位,以及 它所指的一切。 (我是 在 a 中使用“闭包”这个词 和我之前的感觉不同 发布。)

  • 此时我们知道所有 仍标记的内存已分配 任何人都无法触及的记忆 来自任何范围内变量的路径。全部 这些对象被指示 撕毁自己,这会破坏 任何循环引用。

垃圾收集的主要目的是让程序员担心他们创建和使用的对象的内存管理,当然有时也无法避免 - 拥有它总是有益的至少大致了解垃圾收集的工作原理。

历史记录:答案的早期版本对delete 运算符的引用不正确。在 JavaScript the delete operator removes a property from an object 中,与 C/C++ 中的 delete 完全不同。

【讨论】:

  • 苹果指南有缺陷:作者错误地使用了delete;例如在第一个示例中,您应该首先通过window.removeEventListener() 删除事件监听器,而不是delete foo,然后使用foo = null 覆盖变量;在 IE 中,如果 foo 是全局的,delete window.foo(但不是 delete foo)也可以工作,但即使那样它也不会在 FF 或 Opera 中
  • 请注意,Eric 的文章应被视为“仅用于历史目的”。但它仍然提供信息。
  • 另请注意 - IE 6 和 7 不要使用非分代标记和清除垃圾收集器。他们使用简单的引用计数垃圾收集器,它更容易受到垃圾收集的循环引用问题的影响。
  • ECMAScript 的 delete 是一元运算符(表达式),而不是语句(即:delete 0, delete 0, delete 3)。用表达式语句表示时看起来像语句。
  • 是的,当时的答案现在已经过时了,截至 2012 年,现代浏览器使用标记/扫描算法。所以它不再依赖于范围。参考:developer.mozilla.org/en-US/docs/Web/JavaScript/…
【解决方案2】:

当涉及到 DOM 对象时要注意循环引用:

Memory leak patterns in 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 实现就无法收集bigString。有几种方法可以解决这个问题,例如在init() 的末尾设置bigString = nulldelete 不适用于局部变量和函数参数:delete 从对象中删除属性,并且变量对象不可访问- 如果您尝试删除局部变量,ES5 在严格模式下甚至会抛出 ReferenceError!)。

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

【讨论】:

  • DOM 循环引用错误是 JScript 特有的——除 IE 外,没有其他浏览器会遇到此问题。事实上,我相当确定 ECMAScript 规范明确指出 GC 必须能够处理这样的循环:-/
  • @olliej:我在ECMAScript spec 中没有看到任何关于 GC 的提及。
【解决方案3】:

引用自博客的好话

DOM 组件是“垃圾收集”的,JScript 组件也是如此,这意味着如果您在任一组件中创建一个对象,然后失去对该对象的跟踪,它最终会被清理掉。

例如:

function makeABigObject() {
var bigArray = new Array(20000);
}

当您调用该函数时,JScript 组件会创建一个可在该函数内访问的对象(名为 bigArray)。但是,一旦函数返回,您就“失去了对 bigArray 的跟踪”,因为无法再引用它。好吧,JScript 组件意识到您已经忘记了它,所以 bigArray 被清理了——它的内存被回收了。同样的事情也适用于 DOM 组件。如果你说document.createElement('div') 或类似的东西,那么 DOM 组件会为你创建一个对象。一旦你不知何故忘记了那个对象,DOM 组件就会清理相关的。

【讨论】:

    【解决方案4】:

    据我所知,当没有对对象的引用时,JavaScript 的对象会定期被垃圾收集。这是自动发生的事情,但如果你想了解更多关于它是如何工作的,在 C++ 级别,看看WebKitV8 source code 是有意义的

    通常您不需要考虑它,但是,在较旧的浏览器中,例如 IE 5.5 和 IE 6 的早期版本,也许还有当前版本,闭包会创建循环引用,如果未选中,最终会耗尽内存。在我所说的关于闭包的特殊情况下,它是当你添加一个对 dom 对象的 JavaScript 引用,以及一个指向 JavaScript 对象的 DOM 对象的引用时。基本上它永远无法被收集,并最终导致操作系统在循环创建崩溃的测试应用程序中变得不稳定。实际上,这些泄漏通常很小,但为了保持代码干净,您应该删除对 DOM 对象的 JavaScript 引用。

    通常最好使用 delete 关键字来立即取消引用您收到的 JSON 数据等大对象,并使用它完成任何您需要做的事情,尤其是在移动 Web 开发中。这会导致 GC 的下一次扫描删除该对象并释放其内存。

    【讨论】:

    • JavaScript -> DOM -> JavaScript 循环引用问题在较新版本的 IE 中解决了吗?如果有,从什么时候开始?我认为它在架构上非常深入,不太可能得到修复。你有任何消息来源吗?
    • 只是轶事。我没有注意到在标准模式下运行的 IE 8 中的疯狂泄漏,而不是损坏模式。我会调整我的回复。
    • @erikkallen:是的,在 IE 8+ 版本中已经修复了 GC 错误,因为旧版本使用非常幼稚的垃圾收集算法,这使得无法 GC 引用的一对对象彼此。较新的mark-and-sweep 风格算法take care of this
    【解决方案5】:

    垃圾回收 (GC) 是一种通过删除不再需要的对象来自动管理内存的形式。

    任何处理内存的进程都遵循以下步骤:

    1 - 分配你需要的内存空间

    2 - 做一些处理

    3 - 释放这个内存空间

    有两种主要算法用于检测哪些对象不再需要。

    引用计数垃圾回收:该算法将“不再需要一个对象”的定义简化为“一个对象没有其他对象引用它”,如果没有引用,该对象将被删除指向它

    Mark-and-sweep algorithm:将每个对象连接到根源。任何对象都不会连接到根或其他对象。此对象将被删除。

    目前大多数现代浏览器都使用第二种算法。

    【讨论】:

    【解决方案6】:

    “在计算机科学中,垃圾收集 (GC) 是一种自动内存管理形式。垃圾收集器,或者只是收集器,尝试回收垃圾,或者回收应用程序永远不会再次访问或改变的对象使用的内存。”

    所有 JavaScript 引擎都有自己的垃圾收集器,它们可能会有所不同。大多数时候你不必与他们打交道,因为他们只是做他们应该做的事。

    编写更好的代码主要取决于您对编程原理、语言和特定实现的了解程度。

    【讨论】:

      【解决方案7】:

      引用类型不将对象直接存储到变量中 它被分配了,所以下面示例中的对象变量实际上并没有 包含对象实例。相反,它持有一个指针(或引用) 对象存在的内存位置。

      var object = new Object();
      

      如果您将一个引用类型变量分配给另一个,则每个变量 获取指针的副本,并且两者仍然引用相同的对象 记忆。

      var object1 = new Object();
      var object2 = object1;
      

      JavaScript 是一种垃圾收集语言,所以你真的不需要 使用引用类型时担心内存分配。然而, 最好取消引用不再需要的对象,以便垃圾 收集器可以释放该内存。最好的方法是设置 对象变量为空。

      var object1 = new Object();
      // do something
      object1 = null; // dereference
      

      取消引用对象在使用数百万个对象的超大型应用程序中尤为重要。

      来自面向对象 JavaScript 的原理 - NICHOLAS C. ZAKAS

      【讨论】:

        【解决方案8】:

        什么是 JavaScript 垃圾回收?

        检查this

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

        在 Javascript 中,您不关心内存分配和释放。整个问题都需要 Javascript 解释器来解决。 Javascript 中仍然可能存在泄漏,但它们是解释器的错误。如果您对此主题感兴趣,可以阅读更多内容 www.memorymanagement.org

        【讨论】:

        • 您链接到的文章中的各种内存管理系统中,哪一个是 JavaScript 使用的? “在 Javascript 中仍然可能存在泄漏,但它们是解释器的错误。” - 这并不意味着 JS 程序员可以简单地忽略整个问题,例如有一个非常知名的 JS 旧版本 IE 中的 DOM 循环引用问题,您可以在 JS 代码中解决。此外,JS 闭包的工作方式是一个设计特性,而不是一个错误,但是如果你“不恰当地”使用闭包,你可以占用比预期更大的内存块(我不是说不要使用它们)。
        • 内存泄漏是 JavaScript 中的野兽。如果您正在编写一个简单的“大学项目”应用程序,那么不用担心。但是当你开始编写高性能的企业级应用程序时,JavaScript 中的内存管理是必须的。
        【解决方案9】:

        在 Windows 上,您可以使用 Drip.exe 查找内存泄漏或检查您的免费内存例程是否有效。

        真的很简单,只要输入一个网站网址,你就会看到集成的IE渲染器的内存消耗。然后点击刷新,如果内存增加,你在网页的某个地方发现了内存泄漏。但这对于查看释放内存的例程是否适用于 IE 也非常有用。

        【讨论】:

          【解决方案10】:

          在 javascript 中,垃圾回收是不确定的,即何时清除对象,或者是否会清除。这适用于强引用的对象。强引用对象受到垃圾收集的保护。

          在 ES12 之后,可以执行以下实现来检查对象何时被垃圾回收。

          要了解更多关于 javascript 中的垃圾收集,您可以使用 ES12 之后提供的 Finalisers。

          let a = new Array(200).fill(true);
          

          构造finaliaser

          const cleanup = new FinalizationRegistry(key => {
            // your code here
          });
          
          cleanup.register(a, 'wewew');
          

          对象 'a' 现在不可访问,垃圾回收后会发生终结器回调

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-11-10
            • 1970-01-01
            • 2011-01-16
            • 2015-03-30
            • 1970-01-01
            • 2017-10-23
            • 2016-12-27
            • 2011-09-11
            相关资源
            最近更新 更多