什么是JavaScript垃圾回收?


Answers:


192

埃里克·利珀特(Eric Lippert)不久前就此主题撰写了一篇详细的博客文章(另外将其与VBScript进行了比较)。更准确地说,他写了有关JScript的信息,尽管它非常类似于JavaScript,但它是Microsoft自己的ECMAScript实现。我可以想象,您可以假设绝大多数行为对于Internet Explorer的JavaScript引擎都是相同的。当然,不同浏览器的实现方式会有所不同,尽管我怀疑您可以采用一些通用原则并将其应用于其他浏览器。

从该页面引用:

JScript使用了非世代的标记清除垃圾收集器。它是这样的:

  • 每个“范围内”的变量都称为“清除剂”。清道夫可以指数字,对象,字符串等。我们维护一个清道夫列表-变量进入作用域时将移入scav列表,超出范围时将其移出scav列表。

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

  • 其次,它清除了清除剂上的标记以及清除剂引用的可传递性关闭。因此,如果清除剂对象引用了非清除剂对象,则我们清除非清除剂及其所引用的所有内容上的位。(我使用“ closure”一词的含义与之前的文章不同。)

  • 至此,我们知道所有仍标记为已分配内存的内存无法通过任何范围内变量的任何路径访问。指示所有这些对象将其自身拆除,这会破坏任何循环引用。

垃圾回收的主要目的是使程序员不必担心他们创建和使用的对象的内存管理,尽管当然有时并不能避免这种情况-至少对垃圾回收的工作原理有个大概的了解总是有益的。

历史记录:答案的较早版本对delete操作员的引用不正确。在JavaScript中delete运算符从object删除属性,并且与deleteC / C ++ 完全不同。


27
苹果指南存在缺陷:指导者使用delete不正确;例如,在第一个示例中delete foo,您应该首先通过而不是通过删除事件侦听器window.removeEventListener(),然后使用foo = null来覆盖变量;在IE中,delete window.foo(但不是delete foofoo在全球范围内也可以使用,但即使如此,在FF或Opera中也无法使用
Christoph 2010年

3
请注意,应将Eric的文章视为“仅出于历史目的”。但这仍然有用。
彼得·伊凡

2
另请注意-IE 6和IE 7不要使用非世代的标记清除垃圾收集器。他们使用简单的引用计数垃圾收集器,该垃圾收集器更容易受到垃圾收集的循环引用问题的影响。
Doug

1
ECMAScript delete是一元运算符(表达式),而不是语句(即:)delete 0, delete 0, delete 3。当由表达式语句表达时,它看起来像语句。
Hydroper

是的,当时的答案现在已经过时了,从2012年开始,现代浏览器使用标记/清除算法。因此,它不再取决于范围。参考:developer.mozilla.org/en-US/docs/Web/JavaScript/…–
sksallaj

52

当涉及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!
    };
}

bigString只要事件处理程序存在,天真的JS实现就无法收集。有几种方法来解决这个问题,比如设置bigString = null在年底init()delete将局部变量和函数参数不工作:delete从对象中移除了属性和变量对象是不可访问- ES5严格模式下甚至会抛出ReferenceError,如果你尝试删除局部变量!)。

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


20
DOM循环引用错误特定于JScript-除了IE,其他浏览器都没有。实际上,我非常确定ECMAScript规范明确声明GC必须能够处理此类循环:-/
olliej

@olliej:我没有在ECMAScript规范中提到GC 。
Janus Troelsen


16

来自博客的好报价

DOM组件是“垃圾收集”的,就像JScript组件一样,这意味着,如果您在任一组件中创建一个对象,然后失去对该对象的跟踪,则最终将对其进行清理。

例如:

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

调用该函数时,JScript组件将创建一个在函数内可访问的对象(名为bigArray)。但是,一旦函数返回,您就“失去了对bigArray的跟踪”,因为无法再引用它了。好了,JScript组件意识到您已经失去了对其的了解,因此清理了bigArray -回收了其内存。DOM组件中也有同样的事情。如果您说document.createElement('div')或类似的内容,那么DOM组件会为您创建一个对象。一旦您以某种方式失去对该对象的跟踪,DOM组件就会清理相关对象。


13

据我所知,当没有对象剩余的引用时,会定期对JavaScript对象进行垃圾回收。它是自动发生的,但是如果您想了解更多有关其工作方式的信息,请在C ++级别上查看一下WebKitV8源代码

通常,您不需要考虑它,但是,在较旧的浏览器(例如IE 5.5和IE 6的早期版本,以及当前版本)中,闭包将创建循环引用,如果不对其进行检查,则最终会占用内存。在我所说的关于闭包的特殊情况下,就是当您将JavaScript引用添加到dom对象,将对象添加到DOM对象时又引用了JavaScript对象。基本上,它永远都不会被收集,最终会导致操作系统在循环导致崩溃的测试应用中变得不稳定。实际上,这些泄漏通常很小,但是为了保持代码的清洁,您应该删除对DOM对象的JavaScript引用。

通常,最好使用delete关键字立即取消对已收到回传的JSON数据之类的大对象的引用,并完成对它的任何处理,尤其是在移动Web开发中。这将导致GC的下一次扫描将删除该对象并释放其内存。


在较新版本的IE中,JavaScript-> DOM-> JavaScript循环引用问题是否已解决?如果是这样,什么时候开始?我认为它在架构上非常深入,不可能修复。你有资料吗?
erikkallen,2009年

只是轶事。我还没有注意到IE 8在标准模式下运行的疯狂泄漏,而不是损坏模式。我会调整我的回应。
Heat Miser

1
@erikkallen:是的,GC错误已在IE 8+版本中修复,因为较旧的版本使用的是非常幼稚的垃圾收集算法,这使得无法对一对引用的对象进行GC。较新的mark-and-sweep样式算法会处理此问题
kumarharsh 2014年

6

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

任何处理内存的过程,请按照下列步骤操作:

1-分配所需的内存空间

2-做一些处理

3-释放此内存空间

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

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

标记扫描算法:将每个对象连接到根源。任何对象都不会连接到根或其他对象。该对象将被删除。

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


1
要添加其来源,请参见MDN:developer.mozilla.org/en-US/docs/Web/JavaScript/…–
Xenos

4

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

所有JavaScript引擎都有自己的垃圾收集器,它们可能有所不同。大多数时候,您不必与它们打交道,因为它们只是按照他们的预期去做。

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


1

什么是JavaScript垃圾回收?

检查一下

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

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


您链接到本文中的各种内存管理系统中的哪一个是JavaScript使用的系统?“在Javascript中仍然可能发生泄漏,但是它们是解释器的错误。” -这并不意味着JS程序员可以简单地忽略整个问题,例如,在较旧版本的IE中存在一个众所周知的JS <-> DOM循环引用问题,您可以在JS代码中解决。同样,JS闭包的工作方式是一种设计功能,而不是错误,但是,如果“不适当地”使用闭包,则可以占用比预期更大的内存块(我不是说不要使用'em')。
nnnnnn 2012年

3
内存泄漏是JavaScript中的野兽。如果您正在编写一个简单的“大学项目”应用程序,那么不用担心。但是,当您开始编写高性能的企业级应用程序时,必须使用JavaScript进行内存管理。
Doug

1

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

这非常简单,只需输入一个网站URL,您就会看到集成IE渲染器的内存消耗。然后单击刷新,如果内存增加,则在网页上的某处发现内存泄漏。但这对于查看IE释放内存的例程是否工作也非常有用。


1

引用类型不会将对象直接存储到为其分配了变量的变量中,因此本示例中的对象变量实际上并不包含对象实例。相反,它持有指向内存中对象所在位置的指针(或引用)

var object = new Object();

如果将一个变量分配给另一个变量,则每个变量都会获取指针的副本,并且两个变量仍引用内存中的同一对象。

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

两个变量指向一个对象

JavaScript是一种垃圾回收语言,因此在使用引用类型时,您实际上不必担心内存分配。但是,最好取消引用不再需要的对象,以便垃圾回收器可以释放该内存。最好的方法是将对象变量设置为null。

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

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

面向对象的JavaScript原理中的内容-NICHOLAS C. ZAKAS

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.