为什么这是JavaScript中的内存泄漏?

时间:2011-09-22 17:51:30

标签: javascript memory-management memory-leaks

我正在IBM的网站上阅读这篇文章(http://www.ibm.com/developerworks/web/library/wa-memleak/)关于JavaScript中的内存泄漏,当我遇到一个看起来不太喜欢泄漏的内存泄漏时:

<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
   var obj = document.getElementById("element");
   obj.onclick=function innerFunction(){
      alert("Hi! I will leak");
   };
   obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
   // This is used to make the leak significant
};
</script>
<button id="element">Click Me</button>
</body>
</html>

我了解所有其他泄漏,但这个突出了。它说DOM和JavaScript对象之间存在循环引用,但我没有看到它。

任何人都可以对此有所了解吗?

编辑:链接似乎已被取消(我甚至刷新了我已经用完的页面并且它已经关闭)。这是Google的缓存(只要持续时间为http://webcache.googleusercontent.com/search?q=cache:kLR-FJUeKv0J:www.ibm.com/developerworks/web/library/wa-memleak/+memory+management+in+javascript&cd=1&hl=en&ct=clnk&gl=us&client=firefox-a

2 个答案:

答案 0 :(得分:6)

onclick的{​​{1}}的赋值创建了一个函数闭包,它保留了innerFunction范围内变量的值。这允许innerFunction中的代码引用它上面的变量,并且是javascript的理想特性(某些其他语言没有的东西)。

innerFunction范围内的变量包括innerFunction。因此,只要存在对此闭包的引用,就会保留该闭包中的变量。它是一次性内存使用,因此它不会随着时间的推移而累积,因此通常不会很重要。但是,如果您将大数据放入其中一个变量中,然后预期它将被释放,那么“是”您将使用比预期更多的浏览器内存。

这可能导致问题的是在这个特定的例子中,你在JS&lt; ==&gt;之间有一个循环引用。 DOM。在JS中,您保留了(在函数闭包中)对obj变量中DOM对象的引用。在DOM对象中,您保留了对JS代码和函数闭包的引用以及对onclick属性的赋值。一些较旧的浏览器非常愚蠢,即使你从DOM中删除“元素”对象,循环引用也会使垃圾收集器不再释放内存。这在现代浏览器中不是问题,因为它们足够智能以查看此循环引用,并且如果没有外部引用它仍然可以释放该对象。

在您的特定代码中,您实际上没有创建泄漏,因为该元素仍在DOM中。 bigString只是你附加到DOM元素的一大块数据,它将保留在那里直到你删除该属性或删除DOM对象。这不是泄漏,只是存储。

如果你这样做,这将成为IE6泄漏的唯一方法:

obj

现在,您已经从DOM中删除了对象(稍后会在代码中删除)并且可能期望释放与其关联的所有内存,但循环引用会阻止它在IE6中发生。您可以通过执行以下操作来解决此问题:

<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
   var obj = document.getElementById("element");
   obj.onclick=function innerFunction(){
      alert("Hi! I will leak");
   };
   obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
   // This is used to make the leak significant

};

// called later in your code
function freeObject() {
   var obj = document.getElementById("element");
   obj.parentNode.removeChild(obj);  // remove object from DOM
}

</script>
<button id="element">Click Me</button>
</body>
</html>

或者,如果你不需要在闭包中使用<html> <body> <script type="text/javascript"> document.write("Program to illustrate memory leak via closure"); window.onload=function outerFunction(){ var obj = document.getElementById("element"); obj.onclick=function innerFunction(){ alert("Hi! I will leak"); }; obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX")); // This is used to make the leak significant }; // called later in your code function freeObject() { var obj = document.getElementById("element"); obj.onclick=null; // clear handler and closure reference obj.parentNode.removeChild(obj); // remove object from DOM } </script> <button id="element">Click Me</button> </body> </html> 引用,你可以完全用以下方法从闭包中取消引用:

obj

实际上,在泄漏的内存使用量很大的情况下,这只是一个有意义的问题。

  1. 如果您有大量数据存储为DOM属性,那么单个对象泄漏可能会泄漏大量数据。这通常是通过不在DOM对象上存储大块数据来解决的。将它们存储在JS中,您可以明确地控制生命周期。
  2. 如果你有一个包含大量javascript交互的长寿页面,那么创建/销毁DOM对象的操作可以完成很多次。例如,无人看管的幻灯片放映可能会反复创建/销毁DOM对象。即使每件物品的少量内存泄漏最终也会累积并导致问题。在我写的幻灯片中,我只是确保我没有在我添加/删除的这些特定DOM对象上放置任何自定义属性,并且在从DOM中删除对象之前删除了所有事件处理程序。这应该保证不会有循环引用它们。
  3. 你在一个大循环中反复进行的任何类型的DOM操作。

答案 1 :(得分:0)

由于IE6错误导致内存泄漏。

obj引用来自innerFunction的{​​{1}}和onclick引用innerFunction,因为它从外部范围捕获obj