內存泄漏問題總結


1、內存的生命周期

  無論你使用那種語言,內存的生命周期基本是都差不多:分配內存 —— 使用內存 —— 釋放內存,以下是生命周期中每一步發生了什么的一個概述:

  Allocate memory —— 操作系統分配內存,允許你的程序使用它。在基礎語言中(例如 C ),這是一個開發者自己處理的明確操作。然而,在高級語言中,它已經為你處理了。

  Use memory —— 現在你就可以使用之前分配好的內存了。當你在代碼中使用變量時,讀 和 寫 的操作正在發生。

  Release memory —— 現在該釋放你不再需要的內存了,以便它們能夠被再次使用。與分配內存的操作一樣,這種操作在基礎語言中是明確執行的。

2、JavaScript 中的分配內存

  現在,我們將要解釋 JavaScript 中第一步(分配內存)是如何工作的。

  JavaScript 解放了開發者處理內存分配的責任——JavaScript 自己在聲明 values 時就做了這件事

var n = 374; // allocates memory for a number
var s = 'sessionstack'; // allocates memory for a string 
var o = { a: 1, b: null }; // allocates memory for an object and its contained values
var a = [1, null, 'str'];  // (like object) allocates memory for the // array and its contained values
function f(a) { return a + 3; } // allocates a function (which is a callable object) // function expressions also allocate an object
someElement.addEventListener('click', function() { someElement.style.backgroundColor = 'blue'; }, false);

  js中使用內存:基本上在 JavaScript 中使用內存的意思就是在內存在進行 讀 和 寫。這個操作可能是一個變量值的讀取或寫入,一個對象屬性的讀取或寫入,甚至時向函數中傳遞參數。

  當內存不需要時釋放內存:大多數的內存管理問題發生在這個階段。這里最困難的任務就是確定內存何時就不再被需要了。它通常需要開發人員確定程序中哪里不再需要這樣的內存,並釋放它。

  高級語言擁有垃圾回收器,它的職責就是追蹤內存分配和使用情況,找到不再被使用的內存,然后自動地釋放它。不幸的是,這個過程只能得到一個近視的值,因為內存是否被需要是不可判定的(不能用算法求解)。大多數垃圾回收器通過判斷內存是否能夠被再次訪問來工作的,例如:指向它的所有變量都超出了作用域。然而,這只能得到一個近似值。因為在任何位置,存儲器位置可能仍然具有指向其范圍的變量,但是它可能將永遠不會被再次訪問了。

3、垃圾回收

  垃圾回收語言中的泄漏的主要原因是不必要的引用。垃圾回收算法依靠的主要概念就是引用。

  引用計數垃圾回收法:如果一個對象指向它的引用對象數為0,那么就該垃圾回收了

  循環依賴造成無法垃圾回收的情況

  標記掃描算法:解決循環依賴的問題

  即對象不可達,就垃圾回收

  該算法由以下步驟組成:

  (1)垃圾回收器構建“roots”列表。Roots 通常是代碼中保留引用的全局變量。在 JavaScript 中,“window” 對象可以作為 root 全局變量示例。

  (2)所有的 roots 被檢查並標記為 active(即不是垃圾)。所有的 children 也被遞歸檢查。從 root 能夠到達的一切都不被認為是垃圾。

  (3)所有為被標記為 active 的內存可以被認為是垃圾了。收集器限制可以釋放這些內存並將其返回到操作系統。

  這個算法優於前一個,因為“一個對象零引用”會讓這個對象不是可達的。反過來就不一定對了,因為存在循環引用。

4、四種常見的內存泄漏

(1)意外的全局變量以及顯示的全局變量

  意外的全局變量,常見的就是:1、未聲明的變量;2、誤用this指針,比如下面

function foo() {
    this.var1 = "potential accidental global";
}
foo();

  解決方案:

  1、為了防止這些錯誤的發生,可以在 JavaScript 文件開頭添加 “use strict”,使用嚴格模式。這樣在嚴格模式下解析 JavaScript 可以防止意外的全局變量。

  2、即使我們討論了如何預防意外全局變量的產生,但是仍然會有很多代碼用顯示的方式去使用全局變量。這些全局變量是無法進行垃圾回收的(除非將它們賦值為 null 或重新進行分配)。特別是用來臨時存儲和處理大量信息的全局變量非常值得關注。如果你必須使用全局變量來存儲大量數據,那么,請確保在使用完之后,對其賦值為 null 或者重新分配。

(2)timers與callbacks

var serverData = loadData(); setInterval(function() { var renderer = document.getElementById('renderer'); if(renderer) { renderer.innerHTML = JSON.stringify(serverData); } }, 5000); //This will be executed every ~5 seconds.

  這個例子闡述着 timers 可能發生的情況:計時器會引用不再需要的節點或數據。

  renderer 可能在將來會被移除,使得 interval 內的整個塊都不再被需要。但是,interval handler 因為 interval 的存活,所以無法被回收(需要停止 interval,才能回收)。如果 interval handler 無法被回收,則它的依賴也不能被回收。這意味着 serverData——可能存儲了大量數據,也不能被回收。在觀察者模式下,重要的是在他們不再被需要的時候顯式地去刪除它們(或者讓相關對象變為不可達)。

  還有一種定時器泄漏如下

var val = 0; for (var i = 0; i < 90000; i++) { var buggyObject = { callAgain: function() { var ref = this; val = setTimeout(function() { ref.callAgain(); }, 90000); } } buggyObject.callAgain();

  如果你想回收buggyObject,給它設為:buggyObject = null;   //雖然你想回收但是timer還在,所以還是回收不了

//解決方法,先停止定時器
clearTimeout(val); buggyObject = null;

  所以一定要是先清除子集的內存,再清除本集的內存,才可以垃圾回收。dom引用造成的泄漏也會出現這樣的問題。

(3)閉包導致的內存泄漏

(4)dom引用

  有時候,在數據結構中存儲 DOM 結構是有用的。假設要快速更新表中的幾行內容。將每行 DOM 的引用存儲在字典或數組中可能是有意義的。當這種情況發生時,就會保留同一 DOM 元素的兩份引用:一個在 DOM 樹種,另一個在字典中。如果將來某個時候你決定要刪除這些行,則需要讓兩個引用都不可達。

  還有一個額外的考慮,當涉及 DOM 樹內部或葉子節點的引用時,必須考慮這一點。假設你在 JavaScript 代碼中保留了對 table 特定單元格(<td>)的引用。有一天,你決定從 DOM 中刪除該 table,但扔保留着對該單元格的引用。直觀地來看,可以假設 GC 將收集除了該單元格之外所有的內容。實際上,這不會發生的:該單元格是該 table 的子節點,並且 children 保持着對它們 parents 的引用。也就是說,在 JavaScript 代碼中對單元格的引用會導致整個表都保留在內存中的。保留 DOM 元素的引用時,需要仔細考慮。

  當原有的DOM被移除時,子結點引用沒有被移除則無法回收

let select = document.querySelector; let treeRef = select('#tree'); let leafRef = select('#leaf');   //在DOM樹中leafRef是treeFre的一個子結點
select('body').removeChild(treeRef);//#tree不能被回收入,因為treeRef還在 // 解決方法
treeRef = null;  //tree還不能被回收,因為葉子結果leafRef還在
leafRef = null;  //現在#tree可以被釋放了

  DOM 插入順序導致內存泄漏

  當動態創建的 2 個不同范圍的 DOM 對象附加到一起的時候,一個臨時的對象會被創建。這個 DOM 對象改變范圍到 document 時,那個臨時對象就沒用了,這個臨時對象沒有被回收將導致內存泄漏。如果我們將這兩個DOM添加到原有的 DOM 對象上就不會產生中間臨時對象。

 


免責聲明!

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



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