一、內存垃圾回收的意義:
????????在不需要字符串、對象的時候,要釋放其所占的內存,否則系統中的內存有限,占用太多會造成系統奔潰。
二、垃圾回收的機制:
????????js會自動回收垃圾內存。
????????垃圾回收:找出那些不再繼續使用的變量,釋放其占用的內存。
????????js會按照固定的時間間隔周期性的執行垃圾回收的操作。
????????全局變量的的生命周期會知道瀏覽器關閉結束,也就是說全局變量不會被當成垃圾回收。
三、怎么回收:標記清除、引用計數。
????????1) 標記清除? (變量)
????????????????目前最常用的垃圾回收機制,也是當前瀏覽器才用的機制。
????????????????原理:
????????????????標記清除中有兩個重要的概念“進入環境”、“離開環境”。“進入環境”:是變量進入執行環境,“離開環境”:是變量完成任務,離開執行環境。
????????????????當聲明一個變量時,這個變量就是進入的執行環境,瀏覽器給加“進入環境”的標記,當離開執行環境時,瀏覽器給它加“離開環境”的標記。并且回收。
????????????????流程:
????????????????1. 垃圾收集器在運行的時候給所有內存中的變量打上標記。
????????????????2. 去掉環境中的變量以及被環境中的變量引用的變量的標記。
????????????????3. 那些還存在標記的變量則被視為準備刪除的變量。
????????????????4. 最后垃圾收集器會執行清除內存的工作,銷毀那些帶標記的值并回收他們占用的內存。
????????2) 引用計數
????????????????跟蹤記錄每個值的引用次數? (值)
????????????????流程:
????????????????1. 聲明一個變量,并給這個變量賦值,這個值的引用次數就是1
????????????????2. 同一個值被賦給另一個變量,這個值的引用次數+1
????????????????3. 變量的值被更改了 那原本那個值的引用次數-1
????????????????4. 引用次數為0時,說明沒辦法訪問這個值了
????????????????5. 垃圾收集器下一次運行時,會釋放引用次數為0的值所占的內存
????????????????這個流程潛在一個問題:循環引用,則引用次數不會為0,將不能被自動回收了。
????????????????循環引用:是指A中包含指向對象B的指針,而對象B中也包含一個指向對象A的引用。
? ????????????????Eg.
? ???????????????????? function ftc(){
? ???????????????????????? var A = new Object();
? ???????????????????????? var B = new Object();
? ???????????????????????? A.property = B;
? ???????????????????????? B.property = A;
? ???????????????????? }
四、內存泄露的原因
????????js雖然有自動回收機制,但是還是有些情況會造成內存泄漏:
????????1. 全局變量不會被自動回收
????????????????function foo(){
????????????????????this.bar2 = "默認綁定this指向全局"? //全局變量 == window.bsr2
????????????????????bar = "全局變量"? // 沒有var聲明的變量也是全局變量 == window.bar
????????????????}
????????????????foo();
????????????????解決辦法:在函數內使用嚴格模式or細心一點
????????????????function foo(){
????????????????????"use strict"
????????????????????this.bar2 = "默認綁定this指向全局"? //全局變量 == window.bsr2
????????????????????bar = "全局變量"? // 沒有var聲明的變量也是全局變量 == window.bar
????????????????}
????????????????foo();
2. 當不需要setInterval或者setTimeout時,定時器沒有被清除。定時器的回調函數以及內部依賴的變量都不能被回收,造成內存泄露。
var someResource = getData();
setInterval(function(){
var node = document.getElementById("id");
if(node){
node.innerHTML = JSON.stringify(someResource);
//定時器也沒有被清除
}
// node、someResource 存儲了大量數據 無法回收
},1000)
解決辦法:定時器結束時,手動清除定時器。
3. 循環引用,上文提到。
? 解決辦法:手動清除變量,釋放內存。
4. 沒有清除的DOM元素引用
var refV = document.getElementById("ID");
document.body.removeChild(refV);//頁面上dom刪除了
console.log(refV);//能輸出值。所以雖然在頁面上dom刪除了,但在js中 這個dom的變量還在 沒有被回收
refV = null;//解決辦法 手動清除
console.log(refV);//可以看到已經被清除了
5. 閉包引起的內存泄漏:閉包實際上非常容易造成JavaScript對象和DOM對象的隱蔽循環引用
? 解決辦法:將事件處理函數定義在外部,解除閉包;或者在定義事件處理函數的外部函數中,刪除對dom的引用,手動回收。
? Eg.1? ?
? function ex(){
? var element = document.getElementById("div1");? // 1
? element.onclick = function(){
? console.log("this is event"); // 2
? }
? }
? ex();
? 以上函數ex中,用匿名函數創建了一個閉包
? 第1處:js對象 element 引用了一個dom對象? ? JS(element) ----> DOM(div1)
? 第2處:dom對象的onclick屬性引用了一個匿名函數,這形成一個閉包,這個匿名函數可以引用整個ex內的所有對象,包括element DOM(div1.onclick) ---->JS(element)
? 由此形成了JavaScript對象和DOM對象的隱蔽循環引用。
? 解決辦法:
? function ex(){
? var element = document.getElementById("div1");? // 1
? element.onclick = function(){
? console.log("this is event"); // 2
? }
? element = null; //添加的語句 刪除對dom的引用,手動回收
? }
Eg.2
function ev(){
var element = document.getElementById("div2")
var myName = "lili"
element.onclick = function(){
console.log(myName)
}
}
ev();
由于js中只有函數才具有獨立的作用域。
以上函數ev中,onclick引用的匿名函數去訪問myName的時候,發現自身作用域中沒有這個myName變量,
所以需要去訪問父作用域中去調用這個myName變量,形成了閉包,所以myName這個變量會一直存在。
myName這個變量在onclick事件運行之后還會頑固的存在在內存中。
解決辦法是運行完成后手動清除 myName = null