淺談 JS 內存洩漏問題

什麼是內存洩漏?

程序的運行需要內存。只要程序提出要求,操作系統或者運行時(runtime)就必須供給內存。

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

我自己是一名從事了多年開發的web前端老程序員,目前辭職在做自己的web前端私人定製課程,今年年初我花了一個月整理了一份最適合2019年學習的web前端學習乾貨,各種框架都有整理,送給每一位前端小夥伴,想要獲取的可以關注我的頭條號並在後臺私信我:前端,即可免費獲取。

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

內存洩漏的識別方法

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

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

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

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

5、然後在 ChartView 上尋找內存急速下降的部分,查看對應的 EventLog,可以從中找到 GC 的日誌。

具體過程如下圖所示:

淺談 JS 內存洩漏問題

內存洩露的常見原因及處理方式

常見原因:

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 節點也是沒用的

與此類似情景還有: 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. 減少層級過多的引用

原文鏈接:https://mp.weixin.qq.com/s/umlEgXrKNdtrgHh58Mliig
作者:小生方勤


分享到:


相關文章: