因為內(nèi)存的大小是有限的叮姑,所以當內(nèi)存不再需要的時候镶摘,我們需要對其進行釋放姥芥,以便騰出更多的內(nèi)存空間兔乞。
在手動管理內(nèi)存的語言中,我們需要通過一些方式自己來釋放不再需要的內(nèi)存凉唐,比如free函數(shù):
并且這種方式對開發(fā)者的要求也很高庸追,并且一不小心就會產(chǎn)生內(nèi)存泄露;
所以大部分現(xiàn)代的編程語言都是有自己的垃圾回收機制:
垃圾回收的英文是Garbage Collection熊榛,簡稱GC锚国;
對于那些不再使用的對象腕巡,我們都稱之為是垃圾玄坦,它需要被回收,以釋放更多的內(nèi)存空間绘沉;
而我們的語言運行環(huán)境煎楣,比如Java的運行環(huán)境JVM,JavaScript的運行環(huán)境js引擎都會內(nèi)存 垃圾回收器车伞;
垃圾回收器我們也會簡稱為GC择懂,所以在很多地方你看到GC其實指的是垃圾回收器;
但是這里又出現(xiàn)了另外一個很關(guān)鍵的問題:GC怎么知道哪些對象是不再使用的呢另玖?
這里就要用到GC的實現(xiàn)以及對應的算法困曙;
常見的GC算法 – 引用計數(shù)
這個算法就是會對我們被引用的對象做計數(shù),比如變量A引用了對象obj,那么obj的引用計數(shù)就+1谦去,變量B也引用了obj,那么就再+1慷丽,當他們都不引用的時候,計數(shù)就-1 -1變成0鳄哭,變成0了說明沒有再被使用了要糊,就可以進行垃圾回收。
這個算法有個很大的弊端就是會產(chǎn)生循環(huán)引用
比如obj1.info=obj2?
? ? ?obj2.info=obj1
這個時候他們兩個是互相引用的妆丘,就算我清楚 obj1和obj2锄俄,他們各自的計數(shù)還是1,就永遠不會被銷毀勺拣,除非手動的把obj1.info賦值為null,obj1被銷毀奶赠,obj2沒有被引用也會被銷毀
標記清除 標記清除的核心思路是可達性(Reachability)
這個算法是設置一個根對象(root object),垃圾回收器會定期從這個根開始药有,找所有從根開始有引用到的對象毅戈,對于哪些沒有引用到的對象,就認為是不可用的對象;
在我們的js中根對象就是GO全局對象了竹祷,從根出發(fā)能訪問到的對象都會做個標記谈跛,而訪問不到的就認為是不可用的對象就會被銷毀,這個算法可以很好的解決引用計數(shù)算法中的循環(huán)引用因為當obj1 2被銷毀后塑陵,就算他們兩個互相引用感憾,但是從根節(jié)點是到達不了這兩個對象的,所以他們兩個就會被銷毀令花;
其他算法優(yōu)化補充
JS引擎比較廣泛的采用的就是可達性中的標記清除算法阻桅,當然類似于V8引擎為了進行更好的優(yōu)化,它在算法的實現(xiàn)細節(jié)上也會
結(jié)合一些其他的算法兼都。
標記整理
和“標記-清除”相似嫂沉;不同的是,回收期間同時會將保留的存儲對象搬運匯集到連續(xù)的內(nèi)存空間扮碧,從而整合空閑空間趟章,避免內(nèi)存碎片化;
比如在內(nèi)存中連續(xù)開辟了5處地址用來存放對象慎王,其中第一第三個被回收蚓土,留下兩個小坑位,這樣隨著越來越多類似情況發(fā)生赖淤,內(nèi)存中就會出現(xiàn)很多碎片化的內(nèi)存空間蜀漆,東一個西一個的,這個時候我如果需要一個很大的內(nèi)存空間咱旱,這些碎片雖然多也不能提供一個很大的空間确丢,采用標記整理算法后,會把還在使用中的內(nèi)存都給搬運到一些連續(xù)的內(nèi)存空間中吐限,把剩下的碎片化內(nèi)存進行整合鲜侥,這樣提高了內(nèi)存的利用效率。
分代收集
分代收集會對把對象分成新舊兩組毯盈,內(nèi)存中有兩個空間分別是新生代和老生代剃毒,當新生代的內(nèi)存在經(jīng)歷兩輪的清理后,如果還發(fā)現(xiàn)有存活的對象搂赋,就把這些對象放在老生代的內(nèi)存里赘阀,然后減少檢查的頻率,這樣下來就提高了性能脑奠,
增量收集
一次性標記整個對象集是一件比較耗費性能的事情基公,并且會在執(zhí)行過程中帶來明顯的延遲,所以引擎試圖將這件工作分成幾部分來做宋欺,逐一進行處理轰豆,這樣會有許多微小的延遲胰伍,而不是一個大的延遲
閑時收集
垃圾回收器只會在CPU空閑的時候嘗試進行運行,減少可能對代碼產(chǎn)生的影響