30

考虑一下来自Mozilla Docs 关于 JavaScript 内存泄漏的引用:

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

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

请用简单明了的方式解释上述泄漏的原因,我没有得到确切的意思。

网站/页面是否因泄漏而面临安全问题?我该如何避免它们?还有哪些其他代码会导致内存泄漏?如何判断何时发生内存泄漏?

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

4

4 回答 4

20

有两个概念可以帮助您理解此示例。

1) 闭包

闭包的定义是每个内部函数都可以访问其父函数的变量和参数。

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

2)功能=内存

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

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

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

综上所述:

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

anon函数会维护对el的访问,而el对函数有访问权,即循环引用,导致IE内存泄漏。

于 2013-04-02T10:20:10.860 回答
9

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

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

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

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

也就是说,如今,大多数 JavaScript 引擎(甚至是 IE 中的引擎)都使用更高级的垃圾收集器,可以更好地识别未使用的变量,使用诸如标记和清除或分代/临时垃圾收集之类的技术。

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

document.getElementById('el').onclick = function() {
    alert('hello world');
}
于 2013-04-02T10:28:59.527 回答
2

另请参阅有关该问题的 MS 文章的更多信息部分:

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

但请注意,与那篇文章中所说的相反(当浏览器转到新页面时,内存将被回收),这篇文章确认 IE 6 中的一个错误导致内存永远泄漏。

于 2013-05-12T00:41:39.433 回答
1

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

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

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

循环引用

Internet Explorer 为此使用自己的垃圾收集方案,与 JavaScript 使用的机制分开。正是两者之间的交互会导致内存泄漏。

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

于 2013-05-15T10:53:36.253 回答