亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript垃圾回收機(jī)制原理總結(jié)深入探究

 更新時(shí)間:2022年10月20日 10:27:07   作者:橘貓吃不胖~  
就像人類會(huì)產(chǎn)生垃圾一樣,程序運(yùn)行過(guò)程中也會(huì)產(chǎn)生垃圾,如果不及時(shí)回收輕則將會(huì)拖慢程序運(yùn)行,重則會(huì)導(dǎo)致系統(tǒng)崩潰,也就是所謂的內(nèi)存泄漏。所以垃圾回收非常必要

1. 垃圾為何要產(chǎn)生并回收

當(dāng)我們寫(xiě)代碼時(shí)創(chuàng)建一個(gè)基本類型、對(duì)象、函數(shù)等,都是需要占用內(nèi)存的,JavaScript基本數(shù)據(jù)類型存儲(chǔ)在棧內(nèi)存中,引用數(shù)據(jù)類型存儲(chǔ)在堆內(nèi)存中,但是引用數(shù)據(jù)類型會(huì)在棧內(nèi)存中存儲(chǔ)一個(gè)實(shí)際對(duì)象的引用。

比如說(shuō)我們創(chuàng)建了一個(gè)person對(duì)象,然后將person對(duì)象重新賦值:

var person = {
    name: "橘貓吃不胖",
    age: 2
}
person = [1, 2, 3];
console.log(person); // [ 1, 2, 3 ]

那么原本堆內(nèi)存給person對(duì)象開(kāi)辟了一個(gè)空間來(lái)存放,棧內(nèi)存中存放了該引用的地址,但是在下一步中,person對(duì)象成為了一個(gè)數(shù)組,也就是說(shuō)引用地址從原來(lái)的對(duì)象變成了數(shù)組,原來(lái)的引用關(guān)系就沒(méi)有了,那么這時(shí)原來(lái)的對(duì)象在堆內(nèi)存中就會(huì)成為一個(gè)垃圾。

產(chǎn)生的垃圾如果很多,而且一直不清理,堆積起來(lái),就會(huì)影響系統(tǒng)的性能,甚至可能造成系統(tǒng)崩潰。

2. 垃圾回收機(jī)制

JavaScript中主要的內(nèi)存管理概念是可達(dá)性。

那什么是可達(dá)性呢,比如說(shuō)定義一個(gè)對(duì)象:

let person = {
    name: "橘貓吃不胖",
    age: 2
}
console.log(person.name, person.age); // 橘貓吃不胖 2

person引用了這個(gè)對(duì)象,通過(guò)person.name可以獲取到“橘貓吃不胖”的值,通過(guò)person.age可以獲取到2,那么這時(shí)就可以認(rèn)為“橘貓吃不胖”和2是可達(dá)的。

person = null;
console.log(person.name, person.age); // TypeError: Cannot read properties of null (reading 'name')

如果將person設(shè)置為null,那么這兩個(gè)值就沒(méi)法獲得了,它們就是不可達(dá)的,這時(shí)JavaScript垃圾回收機(jī)制就會(huì)自動(dòng)從內(nèi)存中將其清除。

那么JavaScript的垃圾回收就是定期找出這些不可達(dá)的對(duì)象,然后將其釋放。那么找出這些不可達(dá)的對(duì)象有兩種常用的策略:

  • 標(biāo)記清除法
  • 引用計(jì)數(shù)法

2.1 標(biāo)記清除法

標(biāo)記清除法分為標(biāo)記和清除兩個(gè)階段,標(biāo)記階段需要從根節(jié)點(diǎn)遍歷內(nèi)存中的所有對(duì)象,并為可達(dá)的對(duì)象做上標(biāo)記,清除階段則把沒(méi)有標(biāo)記的對(duì)象(非可達(dá)對(duì)象)銷毀。

標(biāo)記清除法的優(yōu)點(diǎn)就是實(shí)現(xiàn)簡(jiǎn)單。

它的缺點(diǎn)有兩個(gè),首先是內(nèi)存碎片化。這是因?yàn)榍謇淼衾?,未被清除的?duì)象內(nèi)存位置是不變的,而被清除掉的內(nèi)存穿插在未被清除的對(duì)象中,導(dǎo)致了內(nèi)存碎片化。

第二個(gè)缺點(diǎn)是內(nèi)存分配速度慢。由于空閑內(nèi)存不是一整塊,假設(shè)新對(duì)象需要的內(nèi)存是size,那么需要對(duì)空閑內(nèi)存進(jìn)行一次單向遍歷,找出大于等于size的內(nèi)存才能為其分配。

標(biāo)記清除算法改進(jìn)—— 標(biāo)記整理算法

標(biāo)記清除算法的缺點(diǎn)主要在于內(nèi)存清理之后剩余的內(nèi)存位置不變而導(dǎo)致內(nèi)存碎片化,因此可以使用標(biāo)記整理算法改進(jìn)。

標(biāo)記整理算法的標(biāo)記階段與標(biāo)記清除算法相同,都是從根節(jié)點(diǎn)遍歷內(nèi)存中的所有對(duì)象,為可達(dá)的對(duì)象打上一個(gè)標(biāo)記。但是在標(biāo)記結(jié)束后,標(biāo)記整理算法將這些可達(dá)的對(duì)象移向內(nèi)存的一端,然后清理掉邊界的內(nèi)存。

2.2 引用計(jì)數(shù)法

引用計(jì)數(shù)法主要記錄對(duì)象有沒(méi)有被其他對(duì)象引用,如果沒(méi)有被引用,它將被垃圾回收機(jī)制回收。它的策略是跟蹤記錄每個(gè)變量值被使用的次數(shù),當(dāng)變量值引用次數(shù)為0時(shí),垃圾回收機(jī)制就會(huì)把它清理掉。

示例代碼如下:

let person = { name: "橘貓吃不胖" }; // { name: "橘貓吃不胖" } 引用次數(shù)為1
let person1 = person; // { name: "橘貓吃不胖" } 引用次數(shù)為2
person = null; // { name: "橘貓吃不胖" } 的引用次數(shù)為1
person1 = null; // { name: "橘貓吃不胖" } 的引用次數(shù)為0

引用計(jì)數(shù)法的優(yōu)點(diǎn)是可以實(shí)現(xiàn)立即進(jìn)行垃圾回收。當(dāng)引用計(jì)數(shù)在引用值為0時(shí),立即進(jìn)行垃圾回收,這樣可以達(dá)到立刻垃圾回收的效果。

它的缺點(diǎn)也有兩個(gè),首先它需要一個(gè)計(jì)數(shù)器,這個(gè)計(jì)數(shù)器可能要占據(jù)很大的位置,因?yàn)槲覀儫o(wú)法知道被引用數(shù)量的多少。

第二個(gè)缺點(diǎn)是無(wú)法解決當(dāng)出現(xiàn)循環(huán)引用時(shí)無(wú)法回收的問(wèn)題。例如a引用了b,b也引用了a,兩個(gè)對(duì)象相互引用,引用計(jì)數(shù)不為0,因此無(wú)法進(jìn)行內(nèi)存清理,如下所示:

let a = { name: "橘貓吃不胖" };
let b = { age: 2 };
a.age = b;
b.name = a;

3. V8對(duì)垃圾回收機(jī)制的優(yōu)化——分代式垃圾回收機(jī)制

目前大多數(shù)瀏覽器都是基于標(biāo)記清除算法,V8進(jìn)行了一些優(yōu)化加工處理,采用分代式垃圾回收機(jī)制。

3.1 新生代與老生代

原本的垃圾回收機(jī)制在每次回收時(shí)都要檢查內(nèi)存中所有的對(duì)象,這樣的話,一些大、老、存活時(shí)間長(zhǎng)的對(duì)象與新、小、存活時(shí)間短的對(duì)象檢查頻率相同,但是前者并不需要頻繁進(jìn)行清理,因此采用分代式垃圾回收機(jī)制。

V8中將堆內(nèi)存分為新生代和老生代兩區(qū)域,采用不同的垃圾回收策略進(jìn)行回收。新生代的對(duì)象為存活時(shí)間較短的對(duì)象,通常只支持1~8M的容量,老生代的對(duì)象為存活時(shí)間較長(zhǎng)或常駐內(nèi)存的對(duì)象,容量通常比較大,V8整個(gè)堆內(nèi)存的大小就等于新生代加上老生代的內(nèi)存。

3.2 新生代的垃圾回收

新生代垃圾回收策略中,將堆內(nèi)存一分為二,一個(gè)是處于使用狀態(tài)的使用區(qū),一個(gè)是處于閑置狀態(tài)的空閑區(qū)。

新加入的對(duì)象都會(huì)存放到使用區(qū),當(dāng)使用區(qū)快滿時(shí),就需要執(zhí)行一次垃圾清理操作,即新生代垃圾回收機(jī)制會(huì)對(duì)使用區(qū)中的活動(dòng)對(duì)象(不需要被清理的對(duì)象)做標(biāo)記,標(biāo)記完成之后將這些活動(dòng)對(duì)象復(fù)制到空閑區(qū)并進(jìn)行排序(避免內(nèi)存碎片化),然后將使用區(qū)清空,原來(lái)的空閑區(qū)變?yōu)槭褂脜^(qū),原來(lái)的使用區(qū)變?yōu)榭臻e區(qū)。

當(dāng)一個(gè)對(duì)象經(jīng)過(guò)多次復(fù)制后依然存活,它將會(huì)被認(rèn)為是生命周期較長(zhǎng)的對(duì)象,會(huì)被移動(dòng)到老生代的內(nèi)存中,或者一個(gè)對(duì)象被復(fù)制到空閑區(qū)時(shí),空閑區(qū)占用空間超過(guò)了25%,那么該對(duì)象也會(huì)進(jìn)入老生代內(nèi)存中。

新生代回收策略——并行回收

JavaScript是單線程的語(yǔ)言,當(dāng)執(zhí)行垃圾回收時(shí),就會(huì)阻塞JavaScript腳本的執(zhí)行,垃圾回收結(jié)束后再繼續(xù)JavaScript腳本執(zhí)行,這種情況叫做全停頓(Stop-The-World)。

如果執(zhí)行一次垃圾回收需要100ms,那么腳本執(zhí)行就得暫停100ms,如果執(zhí)行垃圾回收的時(shí)間過(guò)長(zhǎng),那么就會(huì)造成頁(yè)面卡頓,帶來(lái)不好的用戶體驗(yàn)。對(duì)于這樣的情況,可以采用并行回收的策略。

并行回收指的是在主線程進(jìn)行垃圾回收時(shí),同時(shí)開(kāi)啟多個(gè)輔助線程一起執(zhí)行垃圾回收。比如說(shuō)一項(xiàng)任務(wù)一個(gè)人需要30天才能完成,那么如果安排兩個(gè)人甚至多個(gè)人,可能10來(lái)天甚至更短的時(shí)間就完成了。實(shí)現(xiàn)并行回收可以大大降低垃圾回收的暫停時(shí)間。

新生代對(duì)象空間就采用并行策略,在執(zhí)行垃圾回收的過(guò)程中,會(huì)啟動(dòng)了多個(gè)線程來(lái)負(fù)責(zé)新生代中的垃圾清理操作,這些線程同時(shí)將對(duì)象空間中的數(shù)據(jù)移動(dòng)到空閑區(qū)域,這個(gè)過(guò)程中由于數(shù)據(jù)地址會(huì)發(fā)生改變,所以還需要同步更新引用這些對(duì)象的指針,此即并行回收。

3.3 老生代的垃圾回收

老生代的垃圾回收操作主要就是標(biāo)記清除算法的步驟了,在標(biāo)記階段標(biāo)記所有的可達(dá)對(duì)象,清除階段清除掉未被標(biāo)記的對(duì)象。又由于該算法會(huì)出現(xiàn)內(nèi)存碎片的問(wèn)題,因此會(huì)使用標(biāo)記整理算法來(lái)優(yōu)化這個(gè)過(guò)程。

老生代回收策略——增量標(biāo)記與惰性清理 ①增量標(biāo)記

增量就是將一次標(biāo)記的過(guò)程,分成了許多次,每執(zhí)行完一次就讓?xiě)?yīng)用邏輯執(zhí)行一會(huì)兒,這樣交替多次后完成垃圾回收。但是這會(huì)隨之而來(lái)新的問(wèn)題,首先是如何暫停每次標(biāo)記去執(zhí)行JavaScript代碼,還有如果標(biāo)記好的對(duì)象在執(zhí)行js中改變了狀態(tài)成為了可達(dá)或者不可達(dá)對(duì)象怎么辦,V8對(duì)這兩個(gè)問(wèn)題對(duì)應(yīng)的解決方案分別是三色標(biāo)記法與寫(xiě)屏障。

a.三色標(biāo)記法

三色標(biāo)記法使用三種顏色白、灰、黑來(lái)標(biāo)記對(duì)象的狀態(tài)。白色表示初始狀態(tài),黑色表示已檢查狀態(tài),灰色表示待檢查狀態(tài)。

它的過(guò)程為:

1、將所有的對(duì)象設(shè)置為白色,然后從root對(duì)象出發(fā),將所有可以訪問(wèn)的對(duì)象標(biāo)記為灰色,并用一個(gè)數(shù)組緩存起來(lái);

2、遍歷該數(shù)組,每次都把要遍歷的對(duì)象標(biāo)記為黑色并移出,并且把他的相鄰節(jié)點(diǎn)都涂成灰色,并放入隊(duì)列,直到隊(duì)列為空

3、繼續(xù)檢查是否有灰色對(duì)象,如果有繼續(xù)放入隊(duì)列然后循環(huán),直到所有的可訪問(wèn)對(duì)象都變成黑色

采用三色標(biāo)記法后,程序在恢復(fù)執(zhí)行時(shí)可以直接判斷當(dāng)前內(nèi)存中有沒(méi)有灰色節(jié)點(diǎn),如果有灰色節(jié)點(diǎn),那么從灰色節(jié)點(diǎn)開(kāi)始繼續(xù)執(zhí)行,如果沒(méi)有,直接進(jìn)入垃圾清理階段。

b.寫(xiě)屏障

寫(xiě)屏障可以解決第二個(gè)問(wèn)題,如果執(zhí)行任務(wù)程序時(shí)內(nèi)存中標(biāo)記好的對(duì)象引用關(guān)系被修改了,比如說(shuō)黑色對(duì)象引用了白色對(duì)象,那么它就會(huì)將白色對(duì)象改成灰色對(duì)象,這樣就可以保證下一次標(biāo)記時(shí)可以正常進(jìn)行。

②惰性清理

增量標(biāo)記完成后,就開(kāi)始清除垃圾。如果當(dāng)前的可用內(nèi)存可以支持快速的執(zhí)行代碼,就沒(méi)必要立即清理內(nèi)存,而且清理時(shí)沒(méi)必要一次性清理完,可以按需清理。

優(yōu)點(diǎn):大大減少了主線程停頓的時(shí)間,讓用戶與瀏覽器交互的過(guò)程變得更加流暢

缺點(diǎn):并沒(méi)有減少主線程的總暫停的時(shí)間,甚至?xí)晕⒃黾?/p>

老生代回收策略——并發(fā)回收

并發(fā)回收指的是主線程在執(zhí)行JavaScript的過(guò)程中,輔助線程能夠在后臺(tái),完成執(zhí)行垃圾回收的操作,輔助線程在執(zhí)行垃圾回收的時(shí)候,主線程也可以自由執(zhí)行

垃圾回收機(jī)制多次閱讀之后,我受益匪淺,因此寫(xiě)該文章記錄一下~

到此這篇關(guān)于JavaScript垃圾回收機(jī)制原理總結(jié)深入探究的文章就介紹到這了,更多相關(guān)JavaScript垃圾回收內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論