前端如何處理內存泄漏


定義

內存泄漏:應用程序不再需要的內存,由於某種原因,內存沒有返回到操作系統或可用內存池中。

原因

1.意外的全局變量

JS 在處理未聲明的變量時,對未聲明的變量的引用會在全局對象內創建一個新變量。這些全局變量是無法進行垃圾回收的(除非將它們賦值為 null 或重新進行分配),所以對於存儲大量數據的全局變量,確保在使用完之后,對其賦值為 null 或者重新分配。

  1.  
    function leak(){
  2.  
    leak= "xxx";//leak成為一個全局變量,不會被回收
  3.  
    }

2.被遺忘的定時器(Timers)或者回調函數(callback)

  1.  
    var someResouce=getData();
  2.  
    setInterval( function(){
  3.  
    var node=document.getElementById('Node');
  4.  
    if(node){
  5.  
    node.innerHTML= JSON.stringify(someResouce)
  6.  
    }
  7.  
    }, 1000)
  8.  
    // 如果 id 為 Node 的元素從 DOM 中移除, 該定時器仍會存在,
  9.  
    // 而且, 因為回調函數中包含對 someResource 的引用, 定時器外面的 someResource 也不會被釋放。

3.閉包函數

  1.  
    function bindEvent(){
  2.  
    var obj=document.createElement("XXX");
  3.  
    obj.onclick= function(){
  4.  
    //Even if it's a empty function
  5.  
    }
  6.  
    }

 

閉包可以維持函數內局部變量,使其得不到釋放。 上例定義事件回調時,由於是函數內定義函數,並且內部函數--事件回調的引用外暴了,形成了閉包。解決之道,將事件處理函數定義在外部,解除閉包,或者在定義事件處理函數的外部函數中,刪除對dom的引用。

  1.  
    //將事件處理函數定義在外部
  2.  
    function onclickHandler(){
  3.  
    //do something
  4.  
    }
  5.  
     
  6.  
    function bindEvent(){
  7.  
    var obj=document.createElement("XXX");
  8.  
    obj.onclick=onclickHandler;
  9.  
    }
  10.  
     
  11.  
     
  12.  
    //在定義事件處理函數的外部函數中,刪除對dom的引用
  13.  
    function bindEvent(){
  14.  
    var obj=document.createElement("XXX");
  15.  
    obj.onclick= function(){
  16.  
    //Even if it's a empty function
  17.  
    }
  18.  
    obj= null;
  19.  
    }
 

4.沒有清理的DOM元素的引用

  1.  
    var elements={
  2.  
    button: document.getElementById("button"),
  3.  
    image: document.getElementById("image"),
  4.  
    text: document.getElementById("text")
  5.  
    };
  6.  
    function doStuff(){
  7.  
    image.src= "http://some.url/image";
  8.  
    button.click():
  9.  
    console.log(text.innerHTML)
  10.  
    }
  11.  
    function removeButton(){
  12.  
    document.body.removeChild(document.getElementById('button'))
  13.  
    }

5.子元素存在引起的內存泄露

黃色是指直接被 js變量所引用,在內存里,紅色是指間接被 js變量所引用,如上圖,refB 被 refA 間接引用,導致即使 refB 變量被清空,也是不會被回收的。子元素 refB 由於 parentNode 的間接引用,只要它不被刪除,它所有的父元素(圖中紅色部分)都不會被刪除。

在舉個例子,假設我們在 JavaScript 代碼中保留了對 table 特定單元格(<td>)的引用。有一天,我們決定從 DOM 中刪除該 table,但仍保留着對該單元格的引用。直觀地來看,可以假設 GC 將收集除了該單元格之外所有的內容。實際上,這是不會發生的,因為該單元格是該 table 的子節點,並且 children 保持着對它們 parents 的引用。也就是說,在 JavaScript 代碼中對單元格的引用會導致整個表都保留在內存中的。

6.IE7/8引用計數使用循環引用產生的問題

這個問題主要是在於IE7/8的垃圾回收機制,使用的是引用計數法,當兩個變量A和B相互引用時,即使應用程序中不再使用這兩個變量,GC也不會將這兩個變量回收。

  1.  
    function fn(){
  2.  
    var a={};
  3.  
    var b={};
  4.  
    a.pro=b;
  5.  
    b.pro=a;
  6.  
    }
  7.  
    fn();

排查

Google Chrome瀏覽器提供了非常強大的JS調試工具,Memory 視圖  profiles 視圖讓你可以對 JavaScript 代碼運行時的內存進行快照,並且可以比較這些內存快照。它還讓你可以記錄一段時間內的內存分配情況。在每一個結果視圖中都可以展示不同類型的列表,但是對我們最有用的是 summary 列表和 comparison 列表。  summary 視圖提供了不同類型的分配對象以及它們的合計大小:shallow size (一個特定類型的所有對象的總和)和 retained size (shallow size 加上保留此對象的其它對象的大小)。distance 顯示了對象到達 GC 根(校者注:最初引用的那塊內存,具體內容可自行搜索該術語)的最短距離。 comparison 視圖提供了同樣的信息但是允許對比不同的快照。這對於找到泄漏很有幫助。

右邊視圖中列出了heap里的對象列表。

  • constructor:類名
  • Distance:對象到根的引用層級距離
  • Objects Count:該類的對象數
  • Shallow Size:對象所占內存(不包含內部引用的其他對象所占的內存)
  • Retained Size:對象所占的總內存(包含····················································)

內存泄漏的排查

將上圖框框切換到comparison(對照)選項,該視圖列出了當前視圖與上一個視圖的對象差異

  • New:新建了多少對象
  • Deleted:回收了多少對象
  • Delta:新建的對象個數減去回收的對象個數

重點看closure(閉包),如果#Delta為正數,則表示創建了閉包函數,如果多個快照中都沒有變負數,則表示沒有銷毀閉包


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM