什么是JavaScript垃圾回收?为了编写更好的代码,对于Web程序员来说,了解JavaScript垃圾回收有什么重要意义?
什么是JavaScript垃圾回收?为了编写更好的代码,对于Web程序员来说,了解JavaScript垃圾回收有什么重要意义?
Answers:
埃里克·利珀特(Eric Lippert)不久前就此主题撰写了一篇详细的博客文章(另外将其与VBScript进行了比较)。更准确地说,他写了有关JScript的信息,尽管它非常类似于JavaScript,但它是Microsoft自己的ECMAScript实现。我可以想象,您可以假设绝大多数行为对于Internet Explorer的JavaScript引擎都是相同的。当然,不同浏览器的实现方式会有所不同,尽管我怀疑您可以采用一些通用原则并将其应用于其他浏览器。
从该页面引用:
JScript使用了非世代的标记清除垃圾收集器。它是这样的:
每个“范围内”的变量都称为“清除剂”。清道夫可以指数字,对象,字符串等。我们维护一个清道夫列表-变量进入作用域时将移入scav列表,超出范围时将其移出scav列表。
垃圾收集器不时地运行。首先,它在每个对象,变量,字符串等(GC跟踪的所有内存)上都打上“标记”。(JScript在内部使用VARIANT数据结构,并且该结构中有许多额外的未使用位,因此我们仅设置其中之一。)
其次,它清除了清除剂上的标记以及清除剂引用的可传递性关闭。因此,如果清除剂对象引用了非清除剂对象,则我们清除非清除剂及其所引用的所有内容上的位。(我使用“ closure”一词的含义与之前的文章不同。)
至此,我们知道所有仍标记为已分配内存的内存无法通过任何范围内变量的任何路径访问。指示所有这些对象将其自身拆除,这会破坏任何循环引用。
垃圾回收的主要目的是使程序员不必担心他们创建和使用的对象的内存管理,尽管当然有时并不能避免这种情况-至少对垃圾回收的工作原理有个大概的了解总是有益的。
历史记录:答案的较早版本对delete
操作员的引用不正确。在JavaScript中,delete
运算符从object删除属性,并且与delete
C / C ++ 完全不同。
delete
不正确;例如,在第一个示例中delete foo
,您应该首先通过而不是通过删除事件侦听器window.removeEventListener()
,然后使用foo = null
来覆盖变量;在IE中,delete window.foo
(但不是delete foo
)foo
在全球范围内也可以使用,但即使如此,在FF或Opera中也无法使用
delete
是一元运算符(表达式),而不是语句(即:)delete 0, delete 0, delete 3
。当由表达式语句表达时,它看起来像语句。
当涉及DOM对象时,请注意循环引用:
请记住,只有在没有有效引用该对象的情况下,才可以回收内存。这是闭包和事件处理程序的常见陷阱,因为某些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
,如果你尝试删除局部变量!)。
如果您关注内存消耗,我建议尽可能避免不必要的关闭。
来自博客的好报价
DOM组件是“垃圾收集”的,就像JScript组件一样,这意味着,如果您在任一组件中创建一个对象,然后失去对该对象的跟踪,则最终将对其进行清理。
例如:
function makeABigObject() {
var bigArray = new Array(20000);
}
调用该函数时,JScript组件将创建一个在函数内可访问的对象(名为bigArray)。但是,一旦函数返回,您就“失去了对bigArray的跟踪”,因为无法再引用它了。好了,JScript组件意识到您已经失去了对其的了解,因此清理了bigArray -回收了其内存。DOM组件中也有同样的事情。如果您说document.createElement('div')
或类似的内容,那么DOM组件会为您创建一个对象。一旦您以某种方式失去对该对象的跟踪,DOM组件就会清理相关对象。
据我所知,当没有对象剩余的引用时,会定期对JavaScript对象进行垃圾回收。它是自动发生的,但是如果您想了解更多有关其工作方式的信息,请在C ++级别上查看一下WebKit或V8源代码
通常,您不需要考虑它,但是,在较旧的浏览器(例如IE 5.5和IE 6的早期版本,以及当前版本)中,闭包将创建循环引用,如果不对其进行检查,则最终会占用内存。在我所说的关于闭包的特殊情况下,就是当您将JavaScript引用添加到dom对象,将对象添加到DOM对象时又引用了JavaScript对象。基本上,它永远都不会被收集,最终会导致操作系统在循环导致崩溃的测试应用中变得不稳定。实际上,这些泄漏通常很小,但是为了保持代码的清洁,您应该删除对DOM对象的JavaScript引用。
通常,最好使用delete关键字立即取消对已收到回传的JSON数据之类的大对象的引用,并完成对它的任何处理,尤其是在移动Web开发中。这将导致GC的下一次扫描将删除该对象并释放其内存。
mark-and-sweep
样式算法会处理此问题。
垃圾回收(GC)是通过删除不再需要的对象来进行自动内存管理的一种形式。
任何处理内存的过程,请按照下列步骤操作:
1-分配所需的内存空间
2-做一些处理
3-释放此内存空间
有两种主要算法用于检测不再需要哪些对象。
引用计数垃圾回收:此算法将“不再需要某个对象”的定义简化为“某个对象没有其他引用它的对象”,如果没有引用指向该对象,则该对象将被删除
标记扫描算法:将每个对象连接到根源。任何对象都不会连接到根或其他对象。该对象将被删除。
当前大多数现代浏览器都使用第二种算法。
什么是JavaScript垃圾回收?
检查一下
为了编写更好的代码,对于Web程序员来说,了解JavaScript垃圾回收有什么重要意义?
在Javascript中,您无需关心内存分配和释放。整个问题都需要Javascript解释器来解决。在Javascript中仍然可能发生泄漏,但是它们是解释器的错误。如果您对此主题感兴趣,可以在www.memorymanagement.org上阅读更多内容。
引用类型不会将对象直接存储到为其分配了变量的变量中,因此本示例中的对象变量实际上并不包含对象实例。相反,它持有指向内存中对象所在位置的指针(或引用)
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