2014-06-25 80 views
2

我正在阅读这篇关于javascript优化的文章。 Articlejavascript间隔内存泄漏

我遇到了这个部分,它告诉我什么时候发生泄漏。但我无法找到称呼它的正确方法,因此不会发生泄漏。这里是我感兴趣的部分。

泄漏最糟糕的地方之一是在循环或setTimeout()/ setInterval(),但这是相当普遍的。 考虑下面的例子。

var myObj = { 
    callMeMaybe: function() { 
     var myRef = this; 
     var val = setTimeout(function() { 
      console.log('Time is running out!'); 
      myRef.callMeMaybe(); 
     }, 1000); 
    } 
}; 

如果我们再运行:

myObj.callMeMaybe(); 

开始计时,我们可以看到每一秒“的时间已经不多了!”如果我们再运行:

myObj = null; 

计时器仍然会启动。 myObj不会被垃圾收集,因为传递给setTimeout的闭包必须保持活动才能执行。反过来,它保存对myObj的引用,因为它捕获了myRef。如果我们将闭包传递给任何其他函数,并保持对它的引用,这将是相同的。

值得一提的是,setTimeout/setInterval调用中的引用(如函数)需要在垃圾收集之前执行并完成。

问题是:你如何正确地做到这一点,使你不泄漏?这与调用clearInterval一样简单吗?这是否泄漏一次或每间隔泄漏一次

+0

请记住'val'在函数外部不可用,所以您需要一些工厂来管理应用程序中的所有计时器。 要调用clearInterval,您需要知道'val'的值 –

回答

1

我不会以任何方式称这是内存泄漏 - 这是简单的垃圾回收做它应该的。没有更多“正确”的方式来做到这一点。

只要您的计时器正在运行,最初由myObj指向的对象仍在使用中。只要没有更多的引用,垃圾收集器就会释放它。设置myObj = null会清除对它的一个引用,但您的正在进行的定时器在myRef中有另一个引用,因此只有在引用它的所有引用都消失后才能进行垃圾收集。

是的,如果你停止你的计时器并设置myObj = null,那么将不会有更多的对象引用,GC将摆脱它。请记住,如果您想从外部停止计时器,则需要提供时间表的访问权限,因为没有外部代码可以到达val,您现在已经存储了该计时器。


如果你有很多其他数据的myObj计时器并不需要访问和你试图允许在定时器继续运行数据被释放,那么你就可以保持相同的结构您现在已经清除了该对象的其他数据(可以删除属性或将属性设置为null),也可以更改代码的结构,以便重复定时器可以通过单独的函数调用启动,但不具有保持对该对象的引用以调用方法。

换句话说,如果您的计时器方法需要访问该对象,则该对象通过垃圾回收器正确保持活动状态。如果计时器方法不需要访问该对象,那么您应该通过其他方式运行重复计时器,这样不会重复调用对象的方法 - 从而允许对象进行垃圾回收。

+0

您是否必须显式调用clearTimer或在setTimeout的情况下是否自动清除? – Leo

+0

@Leo:你可以设置多个超时设置,所以'setTimeout'不会与已经设置的那个混淆。你必须调用'clearTimeout'。或者,您可以检查'myObj'是否等于'myRef',并且在这种情况下不需要再次调用'setTimeout'。 (你应该可以通过闭包访问'myObj')。但是请注意,如果你这样做,那么如果你想要额外的超时,你必须将它存储在一个不同的变量中。 – cHao

+1

@Leo:回调一旦被触发就会被释放(当然,除非你通过再次调用'setTimeout'来重新附加它)。所以如果这是唯一持有引用的东西,它会变成垃圾。 – cHao