簡體   English   中英

setTimeout() 如何在這段代碼中造成內存泄漏?

[英]How does setTimeout() create a memory leak in this code?

我正在查看本演示文稿中的幻燈片: http : //slid.es/gruizdevilla/memory

在其中一張幻燈片上,此代碼顯示了它會造成內存泄漏的建議:

var buggyObject = {
   callAgain: function() {
     var ref = this;
     var val = setTimeout(function() {
        ref.callAgain();
     }, 1000);
   }
}

buggyObject.callAgain();
buggyObject = null;

有人可以在這里更詳細地解釋這個問題嗎? 我可能在這里遺漏了一些微妙之處。

這絕對是內存泄漏。 但是,內存消耗如此之小,無法衡量。 我對源代碼做了一些小的改動。

  • 我將整個代碼放在一個循環中以創建相同的場景 100,000 次
  • 我將計時器間隔增加到大約 16 分鍾。 這可以防止瀏覽器崩潰

這是代碼:

for (var i = 0; i < 100000; i++) {
    var buggyObject = {
       callAgain: function() {
         var ref = this;
         var val = setTimeout(function() {
            ref.callAgain(); 
         }, 1000000); //16m
       }
    }

    buggyObject.callAgain();
    buggyObject = null;
}

Chrome 內存泄漏

我的實驗:

我在Chrome 版本 34.0.1847.116 m 中運行代碼並使用 Developer Tools\\Timeline 捕獲內存更改。

正如我們在圖片中看到的,運行這段代碼消耗了大約 32 MB 的內存,一段時間后它減少到大約 30 MB 並保持不變(參見 #1)。 在 Chrome 嘗試多次垃圾回收(參見 #2)和我手動強制垃圾回收(參見 #3、#4)之后,內存消耗保持不變。 不再有buggyObject並且我們無法釋放內存。 唯一可能的方法是關閉瀏覽器。

這是什么原因造成的?

這種行為的主要原因是計時器。 計時器回調及其綁定對象,直到超時發生才會釋放 buggyObject。 在我們的例子中,定時器會重置自己並永遠運行,因此即使沒有對原始對象的引用,它的內存空間也永遠不會被回收。

還有另一個問題描述了 setTimeout() 看起來如何有內存泄漏,但實際上並沒有。

但是,我認為作者想說的是,由於buggyObject創建了一個調用自身的 setTimeout,即使您將 buggyObject 更改為等於null (說您已完成該對象並且可以對其進行清理),該對象也不會'不會被垃圾收集,因為在 setTimeout() 中仍然有對它的引用。 這在技術上是內存泄漏,因為不再有對 setTimeout 函數的任何直接引用,因此您可以稍后清除它(如果您願意的話,這有點像僵屍超時)。

正如advncd 指出的那樣,計時器被執行並在堆棧上添加更多數據。 對發生的事情有一個概念性的看法:

var a = 123;
// call the setTimeout.function
  var a = 123;
  // call the setTimeout.function
    var a = 123;
    // call the setTimeout.function
      var a = 123;
      // call the setTimeout.function
        var a = 123;
        // call the setTimeout.function
          var a = 123;
          // call the setTimeout.function
            var a = 123;
            // call the setTimeout.function
              var a = 123;
              // call the setTimeout.function
                var a = 123;
                ...etc...

因此,每次在永遠增長的堆棧上分配一個新變量a

然而,advncd 沒有提到的是,您有一個 setInterval() 函數來完成您需要做的事情:一遍又一遍地調用相同的函數。 現在你仍然有一個“內存泄漏”,但只有初始化參數泄漏(即它不會在每次計時器超時時增長。)

所以從概念上講,調用是平坦的,您可以避免泄漏:

a = 123;
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
// call the setTimeout.function
...etc...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM