[JavaScript] V8引擎:垃圾回收與內(nèi)存控制

V8的垃圾回收算法

JavaScript的對象在V8引擎的中創(chuàng)建,V8會自動回收不被引用的對象。采用這種方式,降低了內(nèi)存管理的負擔,但也造成了一些不便。

V8在執(zhí)行垃圾回收的時候會阻塞JavaScript代碼的執(zhí)行,因此,堆內(nèi)存的大小就需要進行限制,堆內(nèi)存過大會導致回收算法執(zhí)行時間過長。

從宏觀上來看,V8的堆分為三個部分:年輕分代,年老分代,大對象空間。

(1)年輕分代

年輕分代采用了復制算法,堆空間一分為二,只有一半處于使用中,另外一半用于垃圾清理。年輕分代主要保存那些生命周期短暫的對象,例如函數(shù)中的局部變量。它們類似于C++中在棧上分配的對象,當函數(shù)返回,調用棧中的局部變量都會被析構掉。V8了解內(nèi)存的使用情況,當發(fā)現(xiàn)內(nèi)存空間不夠,需要清理時,才進行回收。

具體步驟是,將還被引用的對象復制到另一半?yún)^(qū)域,然后釋放當前一半的空間,把當前被釋放的空間留作備用,兩者角色互換。年輕分代類似于線程的??臻g,本身不會太大,占用它空間的對象類似于C++中的局部對象,生命周期非常短,因此,大部分都是需要被清理掉的,需要復制的對象極少。雖然犧牲了部分內(nèi)存,但速度極快。

在C++程序中,當調用一個函數(shù)時,函數(shù)內(nèi)部定義的局部對象會占用??臻g,棧空間是有限的,因此,函數(shù)的嵌套也是有限的。隨著函數(shù)調用的結束,??臻g也被釋放掉。而JavaScript代碼,對象使用的空間是在年輕分代中分配,當要在堆中分配而內(nèi)存不夠時,由于新對象的擠壓,會將超出生命期的垃圾對象清除出去。

(2)年老分代

年老分代的對象類似于C++中使用new操作符在堆中分配的對象。這類對象一般不會隨著函數(shù)的退出而銷毀,因此生命期較長。年老分代的大小遠大于年輕分代,主要包含如下數(shù)據(jù):從年輕分代中移動過來的對象,JIT之后產(chǎn)生的代碼,全局對象。

年老分代中需要回收的對象比例極小,如果采用年輕分代一樣的清理算法,會對導致很多不需要清理的對象被復制,所以,年老分代采用了不同的垃圾回收算法。

年老分代,采用了標記清除+標記整理算法,將垃圾回收分為兩個過程。標記清除階段,遍歷堆中的所有對象,把有效的對象標記出來,之后清除垃圾對象。當執(zhí)行完一次標記清除后,堆內(nèi)存變得不連續(xù),內(nèi)存碎片的存在使得不能有效使用內(nèi)存。在后續(xù)的執(zhí)行中,當遇到?jīng)]有一塊碎片內(nèi)存能夠滿足申請對象需要的內(nèi)存空間時,將會觸發(fā)V8執(zhí)行標記整理算法。標記整理移動對象,緊縮V8的堆空間,將碎片的內(nèi)存整理為大塊內(nèi)存。

實際上,V8執(zhí)行這些算法的時候,并不是一次完成的,而是走走停停,因為垃圾回收會阻塞JavaScript代碼的運行,所以采取交替運行的方式,有效的減少了垃圾回收給程序造成的最大停頓時間。

(3)大對象空間

大對象空間主要存放需要較大內(nèi)存的對象,也包括數(shù)據(jù)和JIT生成的代碼。垃圾回收不會移動大對象,這部分內(nèi)存使用的特點是,整塊分配,一次性整塊回收。


參考

Node.js進階之路

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容