DOM:为什么这是内存泄漏?

时间:2013-04-02 09:53:14

标签: javascript internet-explorer memory-leaks garbage-collection circular-reference

请考虑the Mozilla Docs on JavaScript memory leaks中的这句话:

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
}
     

上面的代码将元素设置为在单击时变为红色。它   还会造成内存泄漏。为什么?因为对el的引用是   无意中陷入了为匿名内心创造的封闭   功能。这将在JavaScript之间创建循环引用   对象(函数)和本机对象(el)。

请以简单明了的方式解释上述泄漏的原因,我没有明确指出。

由于泄漏,网站/网页是否面临安全问题?我该如何避免它们?其他什么代码会导致内存泄漏?如何判断内存泄漏的时间?

我是内存泄漏主题的绝对新手。有人可以一步一步地为我澄清这些东西吗?也有人可以帮我澄清这句话“这会在JavaScript对象(函数)和本机对象(el)之间创建一个循环引用。”

4 个答案:

答案 0 :(得分:19)

有两个概念可以帮助您理解这个例子。

1)闭包

闭包的定义是 Every inner function enjoys access to its parent's function variables and parameters.

addHandler()函数完成时,匿名函数仍然可以访问父变量el

2)功能=记忆

每次定义function时,都会创建一个新对象。 是什么让这个例子有点混乱,onclick是一个只能设置为DOM元素一次的事件。

所以el.onclick = function(){};肯定会覆盖旧功能吗?

错误!每次运行addHandler时,都会创建一个新的函数对象。

总结:

每次函数运行时,它都会创建一个新对象,其中包含el的闭包。看到匿名函数维护对el的访问,垃圾收集器无法将其从内存中删除。

anon函数将保持对el的访问,并且el可以访问该函数,即循环引用,这会导致IE中的内存泄漏。

答案 1 :(得分:8)

每当您在JavaScript中定义函数时,都会为其创建execution context;此执行上下文包含对作用域链中所有变量的引用,从全局作用域一直到本地作用域:

function test()
{
    var el = document.getElementById('el');
    el.onclick = function() {
        // execution context of this function: el, test
        alert('hello world');
    }
}

test()完成时,匿名函数还没有被回收,因为它现在被分配给DOM的一个元素;即它是由DOM元素的属性引用

同时,DOM元素本身也是函数执行上下文的一部分,现在由于循环引用而无法再循环,即使实际使用它并不是很明显;你可以在this answer找到演示。

那就是说,现在,大多数JavaScript引擎(甚至是那些在IE中找到的引擎)使用更高级的garbage collector,可以使用诸如mark-and-sweep或世代/短暂的技术更好地识别未使用的变量垃圾收集。

为了确保您不会在任何浏览器上遇到问题(但是,由于页面的典型生命周期,这主要是理论上的):

document.getElementById('el').onclick = function() {
    alert('hello world');
}

答案 2 :(得分:2)

另请参阅有关此问题的MS文章的more information部分:

  

由于DOM对象是非JScript对象,因此发生此内存泄漏。   DOM对象不在标记和清除垃圾收集方案中   JScript中。因此,DOM对象和。之间的循环引用   在浏览器完全打开之前,JScript处理程序不会被破坏   撕下页面。

但请注意,与该文章中所述相反(当浏览器转到新页面时将回收内存),this article确认IE 6中的错误导致内存永久泄露。

答案 3 :(得分:1)

JavaScript的内存管理通常是这样的:“只要有可能达到它,就保持它”。这基本上是任何垃圾收集驱动的内存模型背后的范例。

垃圾收集器往往非常擅长他们的工作,他们甚至会检测某组元素是否只能在这组元素中到达。这些组也称为循环引用,因为如果您按照引用进行操作,则最终会访问您已访问过的元素:您已经运行了一个圆圈。

但是,在您的示例中,您实际上有两个来自两个不同“世界”的对象:

Circular references

  

Internet Explorer使用自己的垃圾收集方案,与JavaScript使用的机制分开。两者之间的相互作用可能导致内存泄漏。

这正是发生的事情,可能导致内存泄漏。