溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

web前端中V8的垃圾回收和內(nèi)存限制如何理解

發(fā)布時(shí)間:2022-01-15 09:47:35 來(lái)源:億速云 閱讀:144 作者:柒染 欄目:移動(dòng)開發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)web前端中V8的垃圾回收和內(nèi)存限制如何理解,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

前言

在第三次瀏覽器大戰(zhàn)中,來(lái)自Google的Chrome瀏覽器憑借優(yōu)異的性能成為聚光燈下的焦點(diǎn)。而Chrome的成功離不開站在其背后的JavaScript引擎V8。

隨著V8的出現(xiàn),讓JavaScript徹底擺脫了作為腳本語(yǔ)言性能低下的形象。V8出色的性能讓JavaScript出現(xiàn)在高性能后臺(tái)服務(wù)程序開發(fā)的舞臺(tái)上。也正是因?yàn)檫@樣的契機(jī),在2009年,Node的創(chuàng)始人Ryan Dahl選擇了V8作為Node的JavaScript腳本引擎。在事件驅(qū)動(dòng)、非阻塞I/O模型的設(shè)計(jì)下實(shí)現(xiàn)了Node。

但是需要了解的是,Node雖然在JavaScript的執(zhí)行上受益于V8,極大的擴(kuò)寬了JavaScript的應(yīng)用場(chǎng)景,讓其應(yīng)用場(chǎng)景從客戶端進(jìn)軍到了服務(wù)端,但是也同時(shí)受到了v8的限制,對(duì)于性能敏感的服務(wù)端的程序,內(nèi)存管理、垃圾回收都會(huì)對(duì)服務(wù)的構(gòu)成產(chǎn)生影響,而這些都和v8有著很大的關(guān)系。

V8的內(nèi)存限制

在Node中如果通過JavaScript使用內(nèi)存操作時(shí)會(huì)發(fā)現(xiàn)實(shí)際只能使用部分內(nèi)存(64位系統(tǒng)下約為1.4G,32位系統(tǒng)下約為0.7G),這種限制對(duì)于其他的服務(wù)端開發(fā)語(yǔ)言來(lái)說基本上都是不存在的。

而V8的這種限制導(dǎo)致的結(jié)果是Node無(wú)法直接操作大內(nèi)存對(duì)象。在單個(gè)Node進(jìn)程的情況下,計(jì)算機(jī)的內(nèi)存資源無(wú)法得到充足的使用。

而問題的原因在于Node是基于V8構(gòu)建,所以在Node中使用對(duì)象都是通過V8自己的方式進(jìn)行分配和管理。

而其內(nèi)存管理機(jī)制在瀏覽器的場(chǎng)景下問題不大,但是對(duì)于Node,卻使得Node有了這般限制。

V8的垃圾回收算法

V8的垃圾回收策略主要基于分代式垃圾回收機(jī)制。

在應(yīng)用中,對(duì)象的生存周期長(zhǎng)短不一,不同的算法只能針對(duì)特定情況產(chǎn)生較為良好的效果。所以在現(xiàn)代的垃圾回收算法中按對(duì)象的存活時(shí)間將內(nèi)存的垃圾回收進(jìn)行不同的分代,然后對(duì)不同分代的內(nèi)存施以更高效的算法。

在V8中,所有的JavaScript對(duì)象都是通過堆來(lái)進(jìn)行分配。我們可以通過process.memoryUsage()這個(gè)方法返回的heapTotal和heapUsed來(lái)查看堆的使用情況,前者是以申請(qǐng)的堆內(nèi)存,后者是當(dāng)前使用的量。如果在代碼中使用的對(duì)象使用的空間超過了申請(qǐng)的空間,那么就會(huì)繼續(xù)申請(qǐng)堆內(nèi)存,直到堆的大小超過V8的限制。

在V8中,主要將堆分為新生代和老生代兩個(gè)區(qū)域,新生代中存放的是生存時(shí)間短的對(duì)象,老生代中存放的生存時(shí)間久的對(duì)象。

新生區(qū)通常只支持 1~8M 的容量,而老生區(qū)支持的容量就大很多了。對(duì)于這兩塊區(qū)域,V8 分別使用兩個(gè)不同的垃圾回收器,以便更高效地實(shí)施垃圾回收。

· 副垃圾回收器,主要負(fù)責(zé)新生代的垃圾回收。

· 主垃圾回收器,主要負(fù)責(zé)老生代的垃圾回收。

不論什么類型的垃圾回收器,它們都有一套共同的執(zhí)行流程。

1. 第一步是標(biāo)記空間中活動(dòng)對(duì)象和非活動(dòng)對(duì)象。所謂活動(dòng)對(duì)象就是還在使用的對(duì)象,非活動(dòng)對(duì)象就是可以進(jìn)行垃圾回收的對(duì)象。

2. 第二步是回收非活動(dòng)對(duì)象所占據(jù)的內(nèi)存。其實(shí)就是在所有的標(biāo)記完成之后,統(tǒng)一清理內(nèi)存中所有被標(biāo)記為可回收的對(duì)象。

3. 第三步是做內(nèi)存整理。一般來(lái)說,頻繁回收對(duì)象后,內(nèi)存中就會(huì)存在大量不連續(xù)空間,我們把這些不連續(xù)的內(nèi)存空間稱為內(nèi)存碎片,。當(dāng)內(nèi)存中出現(xiàn)了大量的內(nèi)存碎片之后,如果需要分配較大連續(xù)內(nèi)存的時(shí)候,就有可能出現(xiàn)內(nèi)存不足的情況。所以最后一步需要整理這些內(nèi)存碎片。(這步其實(shí)是可選的,因?yàn)橛械睦厥掌鞑粫?huì)產(chǎn)生內(nèi)存碎片).

新生代中的垃圾回收

新生代中用Scavenge 算法來(lái)處理,把新生代空間對(duì)半劃分為兩個(gè)區(qū)域,一半是對(duì)象區(qū)域,一半是空閑區(qū)域。新加入的對(duì)象都會(huì)存放到對(duì)象區(qū)域,當(dāng)對(duì)象區(qū)域快被寫滿時(shí),就需要執(zhí)行一次垃圾清理操作。

在垃圾回收過程中,首先要對(duì)對(duì)象區(qū)域中的垃圾做標(biāo)記;標(biāo)記完成之后,就進(jìn)入垃圾清理階段,副垃圾回收器會(huì)把這些存活的對(duì)象復(fù)制到空閑區(qū)域中,同時(shí)它還會(huì)把這些對(duì)象有序地排列起來(lái),所以這個(gè)復(fù)制過程,也就相當(dāng)于完成了內(nèi)存整理操作,復(fù)制后空閑區(qū)域就沒有內(nèi)存碎片了。

完成復(fù)制后,對(duì)象區(qū)域與空閑區(qū)域進(jìn)行角色翻轉(zhuǎn),也就是原來(lái)的對(duì)象區(qū)域變成空閑區(qū)域,原來(lái)的空閑區(qū)域變成了對(duì)象區(qū)域。這樣就完成了垃圾對(duì)象的回收操作,同時(shí)這種角色翻轉(zhuǎn)的操作還能讓新生代中的這兩塊區(qū)域無(wú)限重復(fù)使用下去.

為了執(zhí)行效率,一般新生區(qū)的空間會(huì)被設(shè)置得比較小,也正是因?yàn)樾律鷧^(qū)的空間不大,所以很容易被存活的對(duì)象裝滿整個(gè)區(qū)域。為了解決這個(gè)問題,JavaScript 引擎采用了對(duì)象晉升策略,也就是經(jīng)過兩次垃圾回收依然還存活的對(duì)象,會(huì)被移動(dòng)到老生區(qū)中。

老生代中的垃圾回收

老生代中用標(biāo)記 - 清除(Mark-Sweep)的算法來(lái)處理。首先是標(biāo)記過程階段,標(biāo)記階段就是從一組根元素開始,遞歸遍歷這組根元素(遍歷調(diào)用棧),在這個(gè)遍歷過程中,能到達(dá)的元素稱為活動(dòng)對(duì)象,沒有到達(dá)的元素就可以判斷為垃圾數(shù)據(jù).然后在遍歷過程中標(biāo)記,標(biāo)記完成后就進(jìn)行清除過程。它和副垃圾回收器的垃圾清除過程完全不同,這個(gè)的清除過程是刪除標(biāo)記數(shù)據(jù)。

清除算法后,會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片。而碎片過多會(huì)導(dǎo)致大對(duì)象無(wú)法分配到足夠的連續(xù)內(nèi)存,于是又產(chǎn)生了標(biāo)記 - 整理(Mark-Compact)算法,這個(gè)標(biāo)記過程仍然與標(biāo)記 - 清除算法里的是一樣的,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存,從而讓存活對(duì)象占用連續(xù)的內(nèi)存塊。

增量標(biāo)記算法和全停頓

由于 JavaScript 是運(yùn)行在主線程之上的,一旦執(zhí)行垃圾回收算法,都需要將正在執(zhí)行的 JavaScript 腳本暫停下來(lái),待垃圾回收完畢后再恢復(fù)腳本執(zhí)行。我們把這種行為叫做全停頓。

在 V8 新生代的垃圾回收中,因其空間較小,且存活對(duì)象較少,所以全停頓的影響不大,但老生代就不一樣了。如果執(zhí)行垃圾回收的過程中,占用主線程時(shí)間過久,主線程是不能做其他事情的。比如頁(yè)面正在執(zhí)行一個(gè) JavaScript 動(dòng)畫,因?yàn)槔厥掌髟诠ぷ?,就?huì)導(dǎo)致這個(gè)動(dòng)畫在垃圾回收過程中無(wú)法執(zhí)行,這將會(huì)造成頁(yè)面的卡頓現(xiàn)象。

為了降低老生代的垃圾回收而造成的卡頓,V8 將標(biāo)記過程分為一個(gè)個(gè)的子標(biāo)記過程,同時(shí)讓垃圾回收標(biāo)記和 JavaScript 應(yīng)用邏輯交替進(jìn)行,直到標(biāo)記階段完成,我們把這個(gè)算法稱為增量標(biāo)記(Incremental Marking)算法.

使用增量標(biāo)記算法,可以把一個(gè)完整的垃圾回收任務(wù)拆分為很多小的任務(wù),這些小的任務(wù)執(zhí)行時(shí)間比較短,可以穿插在其他的 JavaScript 任務(wù)中間執(zhí)行,這樣當(dāng)執(zhí)行上述動(dòng)畫效果時(shí),就不會(huì)讓用戶因?yàn)槔厥杖蝿?wù)而感受到頁(yè)面的卡頓了。

關(guān)于web前端中V8的垃圾回收和內(nèi)存限制如何理解就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI