JavaScirpt垃圾回收機(jī)制
JavaScript具有自動(dòng)垃圾回收機(jī)制,也就是說枚驻,執(zhí)行環(huán)境會(huì)負(fù)責(zé)管理代碼執(zhí)行過程中使用的內(nèi)存钝凶。
在C和C++中,開發(fā)人員的一項(xiàng)很重要的任務(wù)就是手工跟蹤內(nèi)存的使用情況痹兜,這是造成許多問題的根源。而在JS中颤诀,內(nèi)存的分配實(shí)現(xiàn)了自動(dòng)的管理字旭。
這種垃圾回收機(jī)制的原理其實(shí)很簡單:找出不再繼續(xù)使用的變量,然后釋放其占用的內(nèi)存崖叫。為此遗淳,垃圾收集器會(huì)按照固定的時(shí)間間隔,周期性地執(zhí)行這一操作心傀。
- 垃圾收集的方式
一屈暗、標(biāo)記清除
這是JS最常用的垃圾收集方式。當(dāng)變量進(jìn)入環(huán)境(eg:在函數(shù)中聲明一個(gè)變量)時(shí),就將它標(biāo)記為“進(jìn)入環(huán)境”养叛。當(dāng)變量離開環(huán)境時(shí)种呐,則將其標(biāo)記為“離開環(huán)境”。
而標(biāo)記的方式有很多種弃甥。比如:像計(jì)算機(jī)組成原理中通過翻轉(zhuǎn)某個(gè)特殊的位爽室,來記錄一個(gè)變量何時(shí)進(jìn)入環(huán)境;或者使用一個(gè)“進(jìn)入/離開環(huán)境的”變量列表來跟蹤哪個(gè)變量發(fā)生了變化淆攻。
垃圾收集器在運(yùn)行的時(shí)候阔墩,會(huì)給存儲(chǔ)在內(nèi)存的所有變量都加上標(biāo)記,然后隨著程序的執(zhí)行瓶珊,慢慢去掉環(huán)境中的變量以及被環(huán)境中變量引用的變量的標(biāo)記啸箫。而在此之后,存有標(biāo)記的變量將被視為準(zhǔn)備刪除的變量伞芹,原因是環(huán)境中的變量已經(jīng)無法訪問到這些變量了筐高。
最后,垃圾收集器完成內(nèi)存清除工作丑瞧,銷毀那些帶標(biāo)記的值柑土,并回收它們所占用的內(nèi)存空間。
目前绊汹,IE稽屏、Firefox、Chrome西乖、Safari和Opera等主流的瀏覽器都使用了標(biāo)記清除式的垃圾收集策略(或類似的策略)狐榔,只不過,垃圾收集的周期略有不同获雕。
堅(jiān)持住薄腻,能看到這行文字,說明你不再是小白了届案。
二庵楷、引用計(jì)數(shù)
這是一種不太常見的垃圾收集策略。簡單地說是跟蹤并記錄每個(gè)值被引用的次數(shù)楣颠。
Netscape Navigator 3.0
是最早使用引用計(jì)數(shù)策略的瀏覽器尽纽,但很快它就遇到了一個(gè)嚴(yán)重的問題:循環(huán)引用。(對(duì)象A中包含一個(gè)指針對(duì)象B的指針童漩,而對(duì)象B中也包含一個(gè)指向?qū)ο驛的引用弄贿。)
function problem(){
var objA = new Object();
var objB = new Ojbect();
objA.someOtherObject = objB;
objB.anotherObject = objA;
}
在
IE9
之前的BOM
和DOM
都不是真正的JS
對(duì)象,因此存在大量的內(nèi)存泄露現(xiàn)象矫膨。導(dǎo)致循環(huán)引用的情況不止這些差凹,其他情況后面再介紹期奔。為了避免類似這樣的循環(huán)引用問題,最好是在不使用它們的時(shí)候危尿,將變量設(shè)置為
null
能庆,切斷變量與它此前引用的值的關(guān)系。
三脚线、性能問題
說到垃圾收集器多長時(shí)間運(yùn)行一次搁胆,不禁讓人聯(lián)想到IE因此聲明狼藉的性能問題。IE的垃圾收集器是根據(jù)內(nèi)存分配量運(yùn)行的邮绿。
具體一點(diǎn)說:就是256個(gè)變量渠旁,4096個(gè)對(duì)象字面量和數(shù)組元素或者64KB的字符串。達(dá)到上述任何一個(gè)值船逮,垃圾收集器就會(huì)運(yùn)行顾腊。很顯然,這種機(jī)制的問題在于挖胃,如果一個(gè)腳本中包含足夠多的變量杂靶,該腳本很可能會(huì)在其生命周期中一直保有那么多的變量。而這樣一來酱鸭,垃圾收集器就不得不頻繁地運(yùn)行吗垮。結(jié)果,由此引發(fā)的嚴(yán)重性能問題促使IE7重寫了垃圾收集機(jī)制凹髓。
事實(shí)上烁登,在有的瀏覽器中可以手動(dòng)觸發(fā)垃圾收集的過程。但通常不建議這么做蔚舀。
在IE中饵沧,調(diào)用window.CollectGarbage();在Opera7以上的版本赌躺,調(diào)用window.opera.collect()方法會(huì)立即執(zhí)行垃圾收集狼牺。
四、管理內(nèi)存
開發(fā)人員一般不必操心內(nèi)存管理的問題礼患,但由于操作系統(tǒng)分配給Web瀏覽器的可用內(nèi)存數(shù)量通常比桌面應(yīng)用程序的少——這樣做的目的主要是出于安全的考慮是钥,目的是防止運(yùn)行網(wǎng)頁時(shí)耗盡系統(tǒng)內(nèi)存而導(dǎo)致系統(tǒng)崩潰。
為了確保占用最少的內(nèi)存讶泰,讓頁面獲得更好的性能咏瑟。力求讓程序在運(yùn)行時(shí),只保存必要的數(shù)據(jù)痪署。一旦數(shù)據(jù)一再有用,最好設(shè)置為null來釋放其引用——這個(gè)做法叫解除引用兄旬。
function createPerson(name){
var localPerson = name;
localPerson.name = name;
return localPerson;
}
var globalPerson = createPerson("learninginto");
//手動(dòng)接觸globalPerson的引用
globalPerson = null;
注意:解除一個(gè)值的引用狼犯,并不意味著自動(dòng)回收該值所占用的內(nèi)存余寥。解除引用的真正作用是讓值脫離執(zhí)行環(huán)境,以便垃圾收集器下次運(yùn)行時(shí)將其回收悯森。
五宋舷、小結(jié)
- 離開作用域的值,將被自動(dòng)標(biāo)記為可以回收瓢姻,因此將在垃圾收集期間被刪除祝蝠。
- “標(biāo)記清除”是目前主流的垃圾收集算法。
- “引用清除”的思想是跟蹤并記錄所有值被引用的次數(shù)幻碱。JS引擎目前都不再使用這種算法绎狭。
- 當(dāng)代碼中存在循環(huán)引用現(xiàn)象時(shí),“引用計(jì)數(shù)”算法會(huì)導(dǎo)致問題褥傍。
- 解除變量的引用不僅有助于消除循環(huán)引用現(xiàn)象儡嘶,而且對(duì)垃圾收集也有好處。為了確保有效地回收內(nèi)存恍风,應(yīng)該及時(shí)解除不再使用的全局對(duì)象蹦狂、全局對(duì)象屬性以及循環(huán)引用變量的引用。