一、內(nèi)存垃圾回收的意義:
????????在不需要字符串闻察、對象的時候拱礁,要釋放其所占的內(nèi)存琢锋,否則系統(tǒng)中的內(nèi)存有限,占用太多會造成系統(tǒng)奔潰呢灶。
二吴超、垃圾回收的機制:
????????js會自動回收垃圾內(nèi)存。
????????垃圾回收:找出那些不再繼續(xù)使用的變量鸯乃,釋放其占用的內(nèi)存鲸阻。
????????js會按照固定的時間間隔周期性的執(zhí)行垃圾回收的操作。
????????全局變量的的生命周期會知道瀏覽器關(guān)閉結(jié)束缨睡,也就是說全局變量不會被當成垃圾回收鸟悴。
三、怎么回收:標記清除奖年、引用計數(shù)细诸。
????????1) 標記清除? (變量)
????????????????目前最常用的垃圾回收機制,也是當前瀏覽器才用的機制陋守。
????????????????原理:
????????????????標記清除中有兩個重要的概念“進入環(huán)境”震贵、“離開環(huán)境”∷溃“進入環(huán)境”:是變量進入執(zhí)行環(huán)境猩系,“離開環(huán)境”:是變量完成任務,離開執(zhí)行環(huán)境中燥。
????????????????當聲明一個變量時寇甸,這個變量就是進入的執(zhí)行環(huán)境,瀏覽器給加“進入環(huán)境”的標記褪那,當離開執(zhí)行環(huán)境時幽纷,瀏覽器給它加“離開環(huán)境”的標記。并且回收博敬。
????????????????流程:
????????????????1. 垃圾收集器在運行的時候給所有內(nèi)存中的變量打上標記友浸。
????????????????2. 去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標記。
????????????????3. 那些還存在標記的變量則被視為準備刪除的變量偏窝。
????????????????4. 最后垃圾收集器會執(zhí)行清除內(nèi)存的工作收恢,銷毀那些帶標記的值并回收他們占用的內(nèi)存。
????????2) 引用計數(shù)
????????????????跟蹤記錄每個值的引用次數(shù)? (值)
????????????????流程:
????????????????1. 聲明一個變量祭往,并給這個變量賦值伦意,這個值的引用次數(shù)就是1
????????????????2. 同一個值被賦給另一個變量,這個值的引用次數(shù)+1
????????????????3. 變量的值被更改了 那原本那個值的引用次數(shù)-1
????????????????4. 引用次數(shù)為0時硼补,說明沒辦法訪問這個值了
????????????????5. 垃圾收集器下一次運行時驮肉,會釋放引用次數(shù)為0的值所占的內(nèi)存
????????????????這個流程潛在一個問題:循環(huán)引用,則引用次數(shù)不會為0已骇,將不能被自動回收了离钝。
????????????????循環(huán)引用:是指A中包含指向?qū)ο驜的指針票编,而對象B中也包含一個指向?qū)ο驛的引用。
? ????????????????Eg.
? ???????????????????? function ftc(){
? ???????????????????????? var A = new Object();
? ???????????????????????? var B = new Object();
? ???????????????????????? A.property = B;
? ???????????????????????? B.property = A;
? ???????????????????? }
四卵渴、內(nèi)存泄露的原因
????????js雖然有自動回收機制慧域,但是還是有些情況會造成內(nèi)存泄漏:
????????1. 全局變量不會被自動回收
????????????????function foo(){
????????????????????this.bar2 = "默認綁定this指向全局"? //全局變量 == window.bsr2
????????????????????bar = "全局變量"? // 沒有var聲明的變量也是全局變量 == window.bar
????????????????}
????????????????foo();
????????????????解決辦法:在函數(shù)內(nèi)使用嚴格模式or細心一點
????????????????function foo(){
????????????????????"use strict"
????????????????????this.bar2 = "默認綁定this指向全局"? //全局變量 == window.bsr2
????????????????????bar = "全局變量"? // 沒有var聲明的變量也是全局變量 == window.bar
????????????????}
????????????????foo();
2. 當不需要setInterval或者setTimeout時,定時器沒有被清除浪读。定時器的回調(diào)函數(shù)以及內(nèi)部依賴的變量都不能被回收昔榴,造成內(nèi)存泄露。
var someResource = getData();
setInterval(function(){
var node = document.getElementById("id");
if(node){
node.innerHTML = JSON.stringify(someResource);
//定時器也沒有被清除
}
// node碘橘、someResource 存儲了大量數(shù)據(jù) 無法回收
},1000)
解決辦法:定時器結(jié)束時互订,手動清除定時器。
3. 循環(huán)引用蛹屿,上文提到屁奏。
? 解決辦法:手動清除變量岩榆,釋放內(nèi)存错负。
4. 沒有清除的DOM元素引用
var refV = document.getElementById("ID");
document.body.removeChild(refV);//頁面上dom刪除了
console.log(refV);//能輸出值。所以雖然在頁面上dom刪除了勇边,但在js中 這個dom的變量還在 沒有被回收
refV = null;//解決辦法 手動清除
console.log(refV);//可以看到已經(jīng)被清除了
5. 閉包引起的內(nèi)存泄漏:閉包實際上非常容易造成JavaScript對象和DOM對象的隱蔽循環(huán)引用
? 解決辦法:將事件處理函數(shù)定義在外部犹撒,解除閉包;或者在定義事件處理函數(shù)的外部函數(shù)中粒褒,刪除對dom的引用,手動回收识颊。
? Eg.1? ?
? function ex(){
? var element = document.getElementById("div1");? // 1
? element.onclick = function(){
? console.log("this is event"); // 2
? }
? }
? ex();
? 以上函數(shù)ex中,用匿名函數(shù)創(chuàng)建了一個閉包
? 第1處:js對象 element 引用了一個dom對象? ? JS(element) ----> DOM(div1)
? 第2處:dom對象的onclick屬性引用了一個匿名函數(shù)奕坟,這形成一個閉包祥款,這個匿名函數(shù)可以引用整個ex內(nèi)的所有對象,包括element DOM(div1.onclick) ---->JS(element)
? 由此形成了JavaScript對象和DOM對象的隱蔽循環(huán)引用月杉。
? 解決辦法:
? 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中只有函數(shù)才具有獨立的作用域刃跛。
以上函數(shù)ev中,onclick引用的匿名函數(shù)去訪問myName的時候苛萎,發(fā)現(xiàn)自身作用域中沒有這個myName變量桨昙,
所以需要去訪問父作用域中去調(diào)用這個myName變量,形成了閉包腌歉,所以myName這個變量會一直存在蛙酪。
myName這個變量在onclick事件運行之后還會頑固的存在在內(nèi)存中。
解決辦法是運行完成后手動清除 myName = null