前端面試查漏補缺--(十三) 記憶體洩漏

shotCat發表於2019-02-24

前言

本系列最開始是為了自己面試準備的.後來發現整理越來越多,差不多有十二萬字元,最後決定還是分享出來給大家.

為了分享整理出來,花費了自己大量的時間,起碼是隻自己用的三倍時間.如果喜歡的話,歡迎收藏,關注我!謝謝!

文章連結

合集篇:

前端面試查漏補缺--Index篇(12萬字元合集) 包含目前已寫好的系列其他十幾篇文章.後續新增值文章不會再在每篇新增連結,強烈建議議點贊,關注合集篇!!!!,謝謝!~

後續更新計劃

後續還會繼續新增設計模式,前端工程化,專案流程,部署,閉環,vue常考知識點 等內容.如果覺得內容不錯的話歡迎收藏,關注我!謝謝!

求一份內推

目前本人也在準備跳槽,希望各位大佬和HR小姐姐可以內推一份靠譜的武漢 前端崗位!郵箱:bupabuku@foxmail.com.謝謝啦!~

什麼是記憶體洩漏?

程式的執行需要記憶體。只要程式提出要求,作業系統或者執行時(runtime)就必須供給記憶體。

對於持續執行的服務程式(daemon),必須及時釋放不再用到的記憶體。否則,記憶體佔用越來越高,輕則影響系統效能:變慢,延遲大等 ,重則導致程式崩潰。

不再用到的記憶體,沒有及時釋放,就叫做記憶體洩漏(memory leak)。

記憶體洩漏的識別方法

  1. 使用快捷鍵 F12 或者 Ctrl+Shift+J 開啟 Chrome 瀏覽器的「開發者工具」。

  2. 選擇 Performance(老版為Timeline) 選項卡,在 Capture 選項中,只勾選 Memory

  3. 設定完成後,點選最左邊的 Record 按鈕,然後就可以訪問網頁了。

  4. 開啟一個網站,例如:www.taobao.com,當網頁載入完成後,點選 Stop,等待分析結果。

  5. 然後在 Chart View 上尋找記憶體急速下降的部分,檢視對應的 Event Log,可以從中找到 GC 的日誌。

具體過程如下圖所示:

前端面試查漏補缺--(十三) 記憶體洩漏

記憶體洩露的常見原因及處理方式

常見原因:

1. 意外的全域性變數

下面程式碼中變數bar在foo函式內,但是bar並沒有宣告.JS就會預設將它變為全域性變數,這樣在頁面關閉之前都不會被釋放.

function foo(){
    bar=2
    console.log('bar沒有被宣告!')
}
複製程式碼

b 沒被宣告,會變成一個全域性變數,在頁面關閉之前不會被釋放.使用嚴格模式可以避免.

2. dom清空時,還存在引用

很多時候,為了方便存取,經常會將 DOM 結點暫時儲存到資料結構中.但是在不需要該DOM節點時,忘記解除對它的引用,則會造成記憶體洩露.

var element = {
  shotCat: document.getElementById('shotCat')
};

document.body.removeChild(document.getElementById('shotCat'));
// 如果element沒有被回收,這裡移除了 shotCat 節點也是沒用的,shotCat 節點依然留存在記憶體中.
複製程式碼

與此類似情景還有: DOM 節點繫結了事件, 但是在移除的時候沒有解除事件繫結,那麼僅僅移除 DOM 節點也是沒用的

3. 定時器中的記憶體洩漏

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);
複製程式碼

如果沒有清除定時器,那麼 someResource 就不會被釋放,如果剛好它又佔用了較大記憶體,就會引發效能問題. 但是 setTimeout ,它計時結束後它的回撥裡面引用的物件佔用的記憶體是可以被回收的. 當然有些場景 setTimeout 的計時可能很長, 這樣的情況下也是需要納入考慮的.

4. 不規範地使用閉包

例如下面的例子: 相互迴圈引用.這是經常容易犯的錯誤,並且有時也不容易發現.

function foo() { 
  var a = {}; 
  function bar() { 
    console.log(a); 
  }; 
  a.fn = bar; 
  return bar; 
};
複製程式碼

bar和a形成了相互迴圈引用.可能有人說bar裡不使用console.log(a)不就沒有引用了嗎就不會造成記憶體洩露了.NONONO,bar作為一個閉包,即使它內部什麼都沒有,foo中的所有變數都還是隱式地被 bar所引用。 即使bar內什麼都沒有還是造成了迴圈引用,那真正的解決辦法就是,不要將a.fn = bar.

避免策略:

  1. 減少不必要的全域性變數,或者生命週期較長的物件,及時對無用的資料進行垃圾回收(即賦值為null);
  2. 注意程式邏輯,避免“死迴圈”之類的 ;
  3. 避免建立過多的物件 原則:不用了的東西要記得及時歸還。
  4. 減少層級過多的引用

Node中的記憶體洩漏

node中的記憶體洩露與JS中的處理方法稍有不同,詳細可以檢視這篇文章,非常的詳細!

感謝及參考

相關文章