
來(lái)源 |?http://www.fly63.com/article/detial/10087
由于字符串、對(duì)象和數(shù)組沒(méi)有固定大小,所有當(dāng)他們的大小已知時(shí),才能對(duì)他們進(jìn)行動(dòng)態(tài)的存儲(chǔ)分配。JavaScript程序每次創(chuàng)建字符串、數(shù)組或?qū)ο髸r(shí),解釋器都必須分配內(nèi)存來(lái)存儲(chǔ)那個(gè)實(shí)體。只要像這樣動(dòng)態(tài)地分配了內(nèi)存,最終都要釋放這些內(nèi)存以便他們能夠被再用,否則,JavaScript的解釋器將會(huì)消耗完系統(tǒng)中所有可用的內(nèi)存,造成系統(tǒng)崩潰這段話解釋了為什么需要系統(tǒng)需要垃圾回收,js不像C/C++,他有自己的一套垃圾回收機(jī)制(Garbage Collection)。JavaScript的解釋器可以檢測(cè)到何時(shí)程序不再使用一個(gè)對(duì)象了,當(dāng)他確定了一個(gè)對(duì)象是無(wú)用的時(shí)候,他就知道不再需要這個(gè)對(duì)象,可以把它所占用的內(nèi)存釋放掉了。例如:var a = "before";var b = "override a";var a = b; //重寫a
這段代碼運(yùn)行之后,“before”這個(gè)字符串失去了引用(之前是被a引用),系統(tǒng)檢測(cè)到這個(gè)事實(shí)之后,就會(huì)釋放該字符串的存儲(chǔ)空間以便這些空間可以被再利用。垃圾回收原理
現(xiàn)在各大瀏覽器通常用采用的垃圾回收有兩種方法:?標(biāo)記清除?、?引用計(jì)數(shù)策略1:標(biāo)記清除
這是javascript中最常用的垃圾回收方式。當(dāng)變量進(jìn)入執(zhí)行環(huán)境是,就標(biāo)記這個(gè)變量為“進(jìn)入環(huán)境”。從邏輯上講,永遠(yuǎn)不能釋放進(jìn)入環(huán)境的變量所占用的內(nèi)存,因?yàn)橹灰獔?zhí)行流進(jìn)入相應(yīng)的環(huán)境,就可能會(huì)用到他們。當(dāng)變量離開(kāi)環(huán)境時(shí),則將其標(biāo)記為“離開(kāi)環(huán)境”。垃圾收集器在運(yùn)行的時(shí)候會(huì)給存儲(chǔ)在內(nèi)存中的所有變量都加上標(biāo)記。然后,它會(huì)去掉環(huán)境中的變量以及被環(huán)境中的變量引用的標(biāo)記。而在此之后再被加上標(biāo)記的變量將被視為準(zhǔn)備刪除的變量,原因是環(huán)境中的變量已經(jīng)無(wú)法訪問(wèn)到這些變量了。最后,垃圾收集器完成內(nèi)存清除工作,銷毀那些帶標(biāo)記的值,并回收他們所占用的內(nèi)存空間垃圾收集器在運(yùn)行的時(shí)候會(huì)給存儲(chǔ)在內(nèi)存中的所有變量都加上標(biāo)記去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記此后再被加上標(biāo)記的變量將被視為準(zhǔn)備刪除的變量,因?yàn)榄h(huán)境中的變量已經(jīng)無(wú)法訪問(wèn)到這些變量了。策略2:引用計(jì)數(shù)
語(yǔ)言引擎有一張”引用表”,保存了內(nèi)存里面所有資源(通常是各種值)的引用次數(shù)。如果一個(gè)值的引用次數(shù)是0,就表示這個(gè)值不再用到了,因此可以將這塊內(nèi)存釋放。上圖中,左下角的兩個(gè)值,沒(méi)有任何引用,所以可以釋放const arr = [1,2,3,4];console.log("hello world");
上面的代碼中,數(shù)組?[1,2,3,4]?是一個(gè)值,會(huì)占用內(nèi)存。變量arr是僅有的對(duì)這個(gè)值的引用,因此引用次數(shù)為1。盡管后面的代碼沒(méi)有用到arr,它是會(huì)持續(xù)占用內(nèi)存如果增加一行代碼,解除arr對(duì)?[1,2,3,4]?引用,這塊內(nèi)存就可以被垃圾回收機(jī)制釋放了。let arr = [1,2,3,4];console.log("hello world");arr = null;
上面代碼中,arr重置為null,就解除了對(duì)?[1,2,3,4]?的引用,引用次數(shù)變成了0,內(nèi)存就可以釋放出來(lái)了。因此,并不是說(shuō)有了垃圾回收機(jī)制,程序員就輕松了。你還是需要關(guān)注內(nèi)存占用:那些很占空間的值,一旦不再用到,你必須檢查是否還存在對(duì)它們的引用。function problem() { var objA = new Object(); var objB = new Object();
objA.someOtherObject = objB; objB.anotherObject = objA;}
在這個(gè)例子中,objA和objB通過(guò)各自的屬性相互引用;也就是說(shuō)這兩個(gè)對(duì)象的引用次數(shù)都是2。在采用引用計(jì)數(shù)的策略中,由于函數(shù)執(zhí)行之后,這兩個(gè)對(duì)象都離開(kāi)了作用域,函數(shù)執(zhí)行完成之后,objA和objB還將會(huì)繼續(xù)存在,因?yàn)樗麄兊囊么螖?shù)永遠(yuǎn)不會(huì)是0。這樣的相互引用如果說(shuō)很大量的存在就會(huì)導(dǎo)致大量的內(nèi)存泄露。不過(guò)上面的問(wèn)題也不是不能解決,我們可以手動(dòng)切斷他們的循環(huán)引用。myObj.element = null;element.someObject =null;
這樣寫代碼的話就可以解決循環(huán)引用的問(wèn)題了,也就防止了內(nèi)存泄露的問(wèn)題。缺點(diǎn)
如果存在循環(huán)引用的情況,那么這個(gè)引用值占的空間就永遠(yuǎn)不會(huì)被回收。