真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

go語言博弈算法 go語言語法

(十一)golang 內(nèi)存分析

編寫過C語言程序的肯定知道通過malloc()方法動(dòng)態(tài)申請內(nèi)存,其中內(nèi)存分配器使用的是glibc提供的ptmalloc2。 除了glibc,業(yè)界比較出名的內(nèi)存分配器有Google的tcmalloc和Facebook的jemalloc。二者在避免內(nèi)存碎片和性能上均比glic有比較大的優(yōu)勢,在多線程環(huán)境中效果更明顯。

十年的利川網(wǎng)站建設(shè)經(jīng)驗(yàn),針對設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營銷型網(wǎng)站建設(shè)的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整利川建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“利川網(wǎng)站設(shè)計(jì)”,“利川網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

Golang中也實(shí)現(xiàn)了內(nèi)存分配器,原理與tcmalloc類似,簡單的說就是維護(hù)一塊大的全局內(nèi)存,每個(gè)線程(Golang中為P)維護(hù)一塊小的私有內(nèi)存,私有內(nèi)存不足再從全局申請。另外,內(nèi)存分配與GC(垃圾回收)關(guān)系密切,所以了解GC前有必要了解內(nèi)存分配的原理。

為了方便自主管理內(nèi)存,做法便是先向系統(tǒng)申請一塊內(nèi)存,然后將內(nèi)存切割成小塊,通過一定的內(nèi)存分配算法管理內(nèi)存。 以64位系統(tǒng)為例,Golang程序啟動(dòng)時(shí)會(huì)向系統(tǒng)申請的內(nèi)存如下圖所示:

預(yù)申請的內(nèi)存劃分為spans、bitmap、arena三部分。其中arena即為所謂的堆區(qū),應(yīng)用中需要的內(nèi)存從這里分配。其中spans和bitmap是為了管理arena區(qū)而存在的。

arena的大小為512G,為了方便管理把a(bǔ)rena區(qū)域劃分成一個(gè)個(gè)的page,每個(gè)page為8KB,一共有512GB/8KB個(gè)頁;

spans區(qū)域存放span的指針,每個(gè)指針對應(yīng)一個(gè)page,所以span區(qū)域的大小為(512GB/8KB)乘以指針大小8byte = 512M

bitmap區(qū)域大小也是通過arena計(jì)算出來,不過主要用于GC。

span是用于管理arena頁的關(guān)鍵數(shù)據(jù)結(jié)構(gòu),每個(gè)span中包含1個(gè)或多個(gè)連續(xù)頁,為了滿足小對象分配,span中的一頁會(huì)劃分更小的粒度,而對于大對象比如超過頁大小,則通過多頁實(shí)現(xiàn)。

根據(jù)對象大小,劃分了一系列class,每個(gè)class都代表一個(gè)固定大小的對象,以及每個(gè)span的大小。如下表所示:

上表中每列含義如下:

class: class ID,每個(gè)span結(jié)構(gòu)中都有一個(gè)class ID, 表示該span可處理的對象類型

bytes/obj:該class代表對象的字節(jié)數(shù)

bytes/span:每個(gè)span占用堆的字節(jié)數(shù),也即頁數(shù)乘以頁大小

objects: 每個(gè)span可分配的對象個(gè)數(shù),也即(bytes/spans)/(bytes/obj)waste

bytes: 每個(gè)span產(chǎn)生的內(nèi)存碎片,也即(bytes/spans)%(bytes/obj)上表可見最大的對象是32K大小,超過32K大小的由特殊的class表示,該class ID為0,每個(gè)class只包含一個(gè)對象。

span是內(nèi)存管理的基本單位,每個(gè)span用于管理特定的class對象, 跟據(jù)對象大小,span將一個(gè)或多個(gè)頁拆分成多個(gè)塊進(jìn)行管理。src/runtime/mheap.go:mspan定義了其數(shù)據(jù)結(jié)構(gòu):

以class 10為例,span和管理的內(nèi)存如下圖所示:

spanclass為10,參照class表可得出npages=1,nelems=56,elemsize為144。其中startAddr是在span初始化時(shí)就指定了某個(gè)頁的地址。allocBits指向一個(gè)位圖,每位代表一個(gè)塊是否被分配,本例中有兩個(gè)塊已經(jīng)被分配,其allocCount也為2。next和prev用于將多個(gè)span鏈接起來,這有利于管理多個(gè)span,接下來會(huì)進(jìn)行說明。

有了管理內(nèi)存的基本單位span,還要有個(gè)數(shù)據(jù)結(jié)構(gòu)來管理span,這個(gè)數(shù)據(jù)結(jié)構(gòu)叫mcentral,各線程需要內(nèi)存時(shí)從mcentral管理的span中申請內(nèi)存,為了避免多線程申請內(nèi)存時(shí)不斷的加鎖,Golang為每個(gè)線程分配了span的緩存,這個(gè)緩存即是cache。src/runtime/mcache.go:mcache定義了cache的數(shù)據(jù)結(jié)構(gòu)

alloc為mspan的指針數(shù)組,數(shù)組大小為class總數(shù)的2倍。數(shù)組中每個(gè)元素代表了一種class類型的span列表,每種class類型都有兩組span列表,第一組列表中所表示的對象中包含了指針,第二組列表中所表示的對象不含有指針,這么做是為了提高GC掃描性能,對于不包含指針的span列表,沒必要去掃描。根據(jù)對象是否包含指針,將對象分為noscan和scan兩類,其中noscan代表沒有指針,而scan則代表有指針,需要GC進(jìn)行掃描。mcache和span的對應(yīng)關(guān)系如下圖所示:

mchache在初始化時(shí)是沒有任何span的,在使用過程中會(huì)動(dòng)態(tài)的從central中獲取并緩存下來,跟據(jù)使用情況,每種class的span個(gè)數(shù)也不相同。上圖所示,class 0的span數(shù)比class1的要多,說明本線程中分配的小對象要多一些。

cache作為線程的私有資源為單個(gè)線程服務(wù),而central則是全局資源,為多個(gè)線程服務(wù),當(dāng)某個(gè)線程內(nèi)存不足時(shí)會(huì)向central申請,當(dāng)某個(gè)線程釋放內(nèi)存時(shí)又會(huì)回收進(jìn)central。src/runtime/mcentral.go:mcentral定義了central數(shù)據(jù)結(jié)構(gòu):

lock: 線程間互斥鎖,防止多線程讀寫沖突

spanclass : 每個(gè)mcentral管理著一組有相同class的span列表

nonempty: 指還有內(nèi)存可用的span列表

empty: 指沒有內(nèi)存可用的span列表

nmalloc: 指累計(jì)分配的對象個(gè)數(shù)線程從central獲取span步驟如下:

將span歸還步驟如下:

從mcentral數(shù)據(jù)結(jié)構(gòu)可見,每個(gè)mcentral對象只管理特定的class規(guī)格的span。事實(shí)上每種class都會(huì)對應(yīng)一個(gè)mcentral,這個(gè)mcentral的集合存放于mheap數(shù)據(jù)結(jié)構(gòu)中。src/runtime/mheap.go:mheap定義了heap的數(shù)據(jù)結(jié)構(gòu):

lock: 互斥鎖

spans: 指向spans區(qū)域,用于映射span和page的關(guān)系

bitmap:bitmap的起始地址

arena_start: arena區(qū)域首地址

arena_used: 當(dāng)前arena已使用區(qū)域的最大地址

central: 每種class對應(yīng)的兩個(gè)mcentral

從數(shù)據(jù)結(jié)構(gòu)可見,mheap管理著全部的內(nèi)存,事實(shí)上Golang就是通過一個(gè)mheap類型的全局變量進(jìn)行內(nèi)存管理的。mheap內(nèi)存管理示意圖如下:

系統(tǒng)預(yù)分配的內(nèi)存分為spans、bitmap、arean三個(gè)區(qū)域,通過mheap管理起來。接下來看內(nèi)存分配過程。

針對待分配對象的大小不同有不同的分配邏輯:

(0, 16B) 且不包含指針的對象: Tiny分配

(0, 16B) 包含指針的對象:正常分配

[16B, 32KB] : 正常分配

(32KB, -) : 大對象分配其中Tiny分配和大對象分配都屬于內(nèi)存管理的優(yōu)化范疇,這里暫時(shí)僅關(guān)注一般的分配方法。

以申請size為n的內(nèi)存為例,分配步驟如下:

Golang內(nèi)存分配是個(gè)相當(dāng)復(fù)雜的過程,其中還摻雜了GC的處理,這里僅僅對其關(guān)鍵數(shù)據(jù)結(jié)構(gòu)進(jìn)行了說明,了解其原理而又不至于深陷實(shí)現(xiàn)細(xì)節(jié)。1、Golang程序啟動(dòng)時(shí)申請一大塊內(nèi)存并劃分成spans、bitmap、arena區(qū)域

2、arena區(qū)域按頁劃分成一個(gè)個(gè)小塊。

3、span管理一個(gè)或多個(gè)頁。

4、mcentral管理多個(gè)span供線程申請使用

5、mcache作為線程私有資源,資源來源于mcentral。

golang面試題2之判斷字符串中字符是否全都不同

請實(shí)現(xiàn) 個(gè)算法,確定 個(gè)字符串的所有字符【是否全都不同】。這 我們要求【不允

許使 額外的存儲(chǔ)結(jié)構(gòu)】。 給定 個(gè)string,請返回 個(gè)bool值,true代表所有字符全都

不同,false代表存在相同的字符。 保證字符串中的字符為【ASCII字符】。字符串的

度 于等于【3000】。

這 有 個(gè)重點(diǎn),第 個(gè)是 ASCII字符 , ASCII字符 字符 共有256個(gè),其中128個(gè)是常

字符,可以在鍵盤上輸 。128之后的是鍵盤上 法找到的。

然后是全部不同,也就是字符串中的字符沒有重復(fù)的,再次,不準(zhǔn)使 額外的儲(chǔ)存結(jié)

構(gòu),且字符串 于等于3000。

如果允許其他額外儲(chǔ)存結(jié)構(gòu),這個(gè)題 很好做。如果不允許的話,可以使 golang內(nèi)置

的 式實(shí)現(xiàn)。

通過 strings.Count 函數(shù)判斷:

使 的是golang內(nèi)置 法 strings.Count ,可以 來判斷在 個(gè)字符串中包含

的另外 個(gè)字符串的數(shù)量

還有不同的方法同樣可以實(shí)現(xiàn),你了解嗎?

推薦go相關(guān)技術(shù) 專欄

gRPC-go源碼剖析與實(shí)戰(zhàn)_帶你走進(jìn)gRPC-go的源碼世界-CSDN博客

【golang詳解】go語言GMP(GPM)原理和調(diào)度

Goroutine調(diào)度是一個(gè)很復(fù)雜的機(jī)制,下面嘗試用簡單的語言描述一下Goroutine調(diào)度機(jī)制,想要對其有更深入的了解可以去研讀一下源碼。

首先介紹一下GMP什么意思:

G ----------- goroutine: 即Go協(xié)程,每個(gè)go關(guān)鍵字都會(huì)創(chuàng)建一個(gè)協(xié)程。

M ---------- thread內(nèi)核級(jí)線程,所有的G都要放在M上才能運(yùn)行。

P ----------- processor處理器,調(diào)度G到M上,其維護(hù)了一個(gè)隊(duì)列,存儲(chǔ)了所有需要它來調(diào)度的G。

Goroutine 調(diào)度器P和 OS 調(diào)度器是通過 M 結(jié)合起來的,每個(gè) M 都代表了 1 個(gè)內(nèi)核線程,OS 調(diào)度器負(fù)責(zé)把內(nèi)核線程分配到 CPU 的核上執(zhí)行

模型圖:

避免頻繁的創(chuàng)建、銷毀線程,而是對線程的復(fù)用。

1)work stealing機(jī)制

當(dāng)本線程無可運(yùn)行的G時(shí),嘗試從其他線程綁定的P偷取G,而不是銷毀線程。

2)hand off機(jī)制

當(dāng)本線程M0因?yàn)镚0進(jìn)行系統(tǒng)調(diào)用阻塞時(shí),線程釋放綁定的P,把P轉(zhuǎn)移給其他空閑的線程執(zhí)行。進(jìn)而某個(gè)空閑的M1獲取P,繼續(xù)執(zhí)行P隊(duì)列中剩下的G。而M0由于陷入系統(tǒng)調(diào)用而進(jìn)被阻塞,M1接替M0的工作,只要P不空閑,就可以保證充分利用CPU。M1的來源有可能是M的緩存池,也可能是新建的。當(dāng)G0系統(tǒng)調(diào)用結(jié)束后,根據(jù)M0是否能獲取到P,將會(huì)將G0做不同的處理:

如果有空閑的P,則獲取一個(gè)P,繼續(xù)執(zhí)行G0。

如果沒有空閑的P,則將G0放入全局隊(duì)列,等待被其他的P調(diào)度。然后M0將進(jìn)入緩存池睡眠。

如下圖

GOMAXPROCS設(shè)置P的數(shù)量,最多有GOMAXPROCS個(gè)線程分布在多個(gè)CPU上同時(shí)運(yùn)行

在Go中一個(gè)goroutine最多占用CPU 10ms,防止其他goroutine被餓死。

具體可以去看另一篇文章

【Golang詳解】go語言調(diào)度機(jī)制 搶占式調(diào)度

當(dāng)創(chuàng)建一個(gè)新的G之后優(yōu)先加入本地隊(duì)列,如果本地隊(duì)列滿了,會(huì)將本地隊(duì)列的G移動(dòng)到全局隊(duì)列里面,當(dāng)M執(zhí)行work stealing從其他P偷不到G時(shí),它可以從全局G隊(duì)列獲取G。

協(xié)程經(jīng)歷過程

我們創(chuàng)建一個(gè)協(xié)程 go func()經(jīng)歷過程如下圖:

說明:

這里有兩個(gè)存儲(chǔ)G的隊(duì)列,一個(gè)是局部調(diào)度器P的本地隊(duì)列、一個(gè)是全局G隊(duì)列。新創(chuàng)建的G會(huì)先保存在P的本地隊(duì)列中,如果P的本地隊(duì)列已經(jīng)滿了就會(huì)保存在全局的隊(duì)列中;處理器本地隊(duì)列是一個(gè)使用數(shù)組構(gòu)成的環(huán)形鏈表,它最多可以存儲(chǔ) 256 個(gè)待執(zhí)行任務(wù)。

G只能運(yùn)行在M中,一個(gè)M必須持有一個(gè)P,M與P是1:1的關(guān)系。M會(huì)從P的本地隊(duì)列彈出一個(gè)可執(zhí)行狀態(tài)的G來執(zhí)行,如果P的本地隊(duì)列為空,就會(huì)想其他的MP組合偷取一個(gè)可執(zhí)行的G來執(zhí)行;

一個(gè)M調(diào)度G執(zhí)行的過程是一個(gè)循環(huán)機(jī)制;會(huì)一直從本地隊(duì)列或全局隊(duì)列中獲取G

上面說到P的個(gè)數(shù)默認(rèn)等于CPU核數(shù),每個(gè)M必須持有一個(gè)P才可以執(zhí)行G,一般情況下M的個(gè)數(shù)會(huì)略大于P的個(gè)數(shù),這多出來的M將會(huì)在G產(chǎn)生系統(tǒng)調(diào)用時(shí)發(fā)揮作用。類似線程池,Go也提供一個(gè)M的池子,需要時(shí)從池子中獲取,用完放回池子,不夠用時(shí)就再創(chuàng)建一個(gè)。

work-stealing調(diào)度算法:當(dāng)M執(zhí)行完了當(dāng)前P的本地隊(duì)列隊(duì)列里的所有G后,P也不會(huì)就這么在那躺尸啥都不干,它會(huì)先嘗試從全局隊(duì)列隊(duì)列尋找G來執(zhí)行,如果全局隊(duì)列為空,它會(huì)隨機(jī)挑選另外一個(gè)P,從它的隊(duì)列里中拿走一半的G到自己的隊(duì)列中執(zhí)行。

如果一切正常,調(diào)度器會(huì)以上述的那種方式順暢地運(yùn)行,但這個(gè)世界沒這么美好,總有意外發(fā)生,以下分析goroutine在兩種例外情況下的行為。

Go runtime會(huì)在下面的goroutine被阻塞的情況下運(yùn)行另外一個(gè)goroutine:

用戶態(tài)阻塞/喚醒

當(dāng)goroutine因?yàn)閏hannel操作或者network I/O而阻塞時(shí)(實(shí)際上golang已經(jīng)用netpoller實(shí)現(xiàn)了goroutine網(wǎng)絡(luò)I/O阻塞不會(huì)導(dǎo)致M被阻塞,僅阻塞G,這里僅僅是舉個(gè)栗子),對應(yīng)的G會(huì)被放置到某個(gè)wait隊(duì)列(如channel的waitq),該G的狀態(tài)由_Gruning變?yōu)開Gwaitting,而M會(huì)跳過該G嘗試獲取并執(zhí)行下一個(gè)G,如果此時(shí)沒有可運(yùn)行的G供M運(yùn)行,那么M將解綁P,并進(jìn)入sleep狀態(tài);當(dāng)阻塞的G被另一端的G2喚醒時(shí)(比如channel的可讀/寫通知),G被標(biāo)記為,嘗試加入G2所在P的runnext(runnext是線程下一個(gè)需要執(zhí)行的 Goroutine。), 然后再是P的本地隊(duì)列和全局隊(duì)列。

系統(tǒng)調(diào)用阻塞

當(dāng)M執(zhí)行某一個(gè)G時(shí)候如果發(fā)生了阻塞操作,M會(huì)阻塞,如果當(dāng)前有一些G在執(zhí)行,調(diào)度器會(huì)把這個(gè)線程M從P中摘除,然后再創(chuàng)建一個(gè)新的操作系統(tǒng)的線程(如果有空閑的線程可用就復(fù)用空閑線程)來服務(wù)于這個(gè)P。當(dāng)M系統(tǒng)調(diào)用結(jié)束時(shí)候,這個(gè)G會(huì)嘗試獲取一個(gè)空閑的P執(zhí)行,并放入到這個(gè)P的本地隊(duì)列。如果獲取不到P,那么這個(gè)線程M變成休眠狀態(tài), 加入到空閑線程中,然后這個(gè)G會(huì)被放入全局隊(duì)列中。

隊(duì)列輪轉(zhuǎn)

可見每個(gè)P維護(hù)著一個(gè)包含G的隊(duì)列,不考慮G進(jìn)入系統(tǒng)調(diào)用或IO操作的情況下,P周期性的將G調(diào)度到M中執(zhí)行,執(zhí)行一小段時(shí)間,將上下文保存下來,然后將G放到隊(duì)列尾部,然后從隊(duì)列中重新取出一個(gè)G進(jìn)行調(diào)度。

除了每個(gè)P維護(hù)的G隊(duì)列以外,還有一個(gè)全局的隊(duì)列,每個(gè)P會(huì)周期性地查看全局隊(duì)列中是否有G待運(yùn)行并將其調(diào)度到M中執(zhí)行,全局隊(duì)列中G的來源,主要有從系統(tǒng)調(diào)用中恢復(fù)的G。之所以P會(huì)周期性地查看全局隊(duì)列,也是為了防止全局隊(duì)列中的G被餓死。

除了每個(gè)P維護(hù)的G隊(duì)列以外,還有一個(gè)全局的隊(duì)列,每個(gè)P會(huì)周期性地查看全局隊(duì)列中是否有G待運(yùn)行并將其調(diào)度到M中執(zhí)行,全局隊(duì)列中G的來源,主要有從系統(tǒng)調(diào)用中恢復(fù)的G。之所以P會(huì)周期性地查看全局隊(duì)列,也是為了防止全局隊(duì)列中的G被餓死。

M0

M0是啟動(dòng)程序后的編號(hào)為0的主線程,這個(gè)M對應(yīng)的實(shí)例會(huì)在全局變量rutime.m0中,不需要在heap上分配,M0負(fù)責(zé)執(zhí)行初始化操作和啟動(dòng)第一個(gè)G,在之后M0就和其他的M一樣了

G0

G0是每次啟動(dòng)一個(gè)M都會(huì)第一個(gè)創(chuàng)建的goroutine,G0僅用于負(fù)責(zé)調(diào)度G,G0不指向任何可執(zhí)行的函數(shù),每個(gè)M都會(huì)有一個(gè)自己的G0,在調(diào)度或系統(tǒng)調(diào)用時(shí)會(huì)使用G0的??臻g,全局變量的G0是M0的G0

一個(gè)G由于調(diào)度被中斷,此后如何恢復(fù)?

中斷的時(shí)候?qū)⒓拇嫫骼锏臈P畔?,保存到自己的G對象里面。當(dāng)再次輪到自己執(zhí)行時(shí),將自己保存的棧信息復(fù)制到寄存器里面,這樣就接著上次之后運(yùn)行了。

我這里只是根據(jù)自己的理解進(jìn)行了簡單的介紹,想要詳細(xì)了解有關(guān)GMP的底層原理可以去看Go調(diào)度器 G-P-M 模型的設(shè)計(jì)者的文檔或直接看源碼

參考: ()

()

golang變量(二)——map和slice詳解

衍生類型,interface{} , map, [] ,struct等

map類似于java的hashmap,python的dict,php的hash array。

常規(guī)的for循環(huán),可以用for k,v :=range m {}. 但在下面清空有一個(gè)坑注意:

著名的map[string]*struct 副本問題

結(jié)果:

Go 中不存在引用傳遞,所有的參數(shù)傳遞都是值傳遞,而map是等同于指針類型的,所以在把map變量傳遞給函數(shù)時(shí),函數(shù)對map的修改,也會(huì)實(shí)質(zhì)改變map的值。

slice類似于其他語言的數(shù)組(list,array),slice初始化和map一樣,這里不在重復(fù)

除了Pointer數(shù)組外,len表示使用長度,cap是總?cè)萘?,make([]int, len, cap)可以預(yù)申請 比較大的容量,這樣可以減少容量拓展的消耗,前提是要用到。

cap是計(jì)算切片容量,len是計(jì)算變量長度的,兩者不一樣。具體例子如下:

結(jié)果:

分析:cap是計(jì)算當(dāng)前slice已分配的容量大小,采用的是預(yù)分配的伙伴算法(當(dāng)容量滿時(shí),拓展分配一倍的容量)。

append是slice非常常用的函數(shù),用于添加數(shù)據(jù)到slice中,但如果使用不好,會(huì)有下面的問題:

預(yù)期是[1 2 3 4 5 6 7 8 9 10], [1 2 3 4 5 6 7 8 9 10 11 12],但實(shí)際結(jié)果是:

注意slice是值傳遞,修改一下:

輸出如下:

== 只能用于判斷常規(guī)數(shù)據(jù)類型,無法使用用于slice和map判斷,用于判斷map和slice可以使用reflect.DeepEqual,這個(gè)函數(shù)用了遞歸來判斷每層的k,v是否一致。

當(dāng)然還有其他方式,比如轉(zhuǎn)換成json,但小心有一些異常的bug,比如html編碼,具體這個(gè)json問題,待后面在分析。

Go 語言三色標(biāo)記掃描對象是 DFS 還是 BFS?

最近在看左神新書 《Go 語言設(shè)計(jì)與實(shí)現(xiàn)》的垃圾收集器時(shí)產(chǎn)生一個(gè)疑惑,花了點(diǎn)時(shí)間搞清楚了記錄一下。

Go 語言垃圾回收的實(shí)現(xiàn)使用了標(biāo)記清除算法,將對象的狀態(tài)抽象成黑色(活躍對象)、灰色(活躍對象中間狀態(tài))、白色(潛在垃圾對象也是所有對象的默認(rèn)狀態(tài))三種,注意沒有具體的字段標(biāo)記顏色。

整個(gè)標(biāo)記過程就是把白色對象標(biāo)黑的過程:

1.首先將 ROOT 根對象(包括全局變量、goroutine 棧上的對象等)放入到灰色集合

2.選一個(gè)灰色對象,標(biāo)成黑色,將所有可達(dá)的子對象放入到灰色集合

3.重復(fù)2的步驟,直到灰色集合中為空

下圖是書上的插圖,看上去是一個(gè)典型的深度優(yōu)先搜索的算法。

下圖是劉丹冰寫的《Golang 修養(yǎng)之路》的插圖,看上去是一個(gè)典型的廣度優(yōu)先搜索的算法。

我疑惑的點(diǎn)在于這個(gè)標(biāo)記過程是深度優(yōu)先算法還是廣度優(yōu)先算法,因?yàn)楹芏辔恼虏┛蛯Υ硕紱]有很清楚的說明,作為學(xué)習(xí)者這種細(xì)節(jié)其實(shí)也不影響對整個(gè) GC 流程的理解,但是這種細(xì)節(jié)我非常喜歡扣:)

對著書和源碼摸索著大致找到了一個(gè)結(jié)果是深度優(yōu)先。下面看下大致的過程,源碼基于1.15.2版本:

gcStart 是 Go 語言三種條件觸發(fā) GC 的共同入口

啟動(dòng)后臺(tái)標(biāo)記任務(wù)

為每個(gè)處理器創(chuàng)建用于執(zhí)行后臺(tái)標(biāo)記任務(wù)的 Goroutine

上面休眠的 G 會(huì)在調(diào)度循環(huán)中檢查并喚醒執(zhí)行

執(zhí)行標(biāo)記

gcw 是每個(gè) P 獨(dú)有的所以不用擔(dān)心并發(fā)的問題 和 GMP、mcache 一樣設(shè)計(jì),減少鎖競爭

嘗試在全局列表中獲取一個(gè)不為空的 buf

這是官方實(shí)現(xiàn)的無鎖隊(duì)列:)漲見識(shí)了,for 循環(huán)加原子操作實(shí)現(xiàn)棧的 pop

到這里從灰色集合中獲取待掃描的對象邏輯說完了。找到對象了接著就是 scanobject(b, gcw) 了,里面有兩段邏輯要注意

根據(jù)索引位置找到對象進(jìn)行標(biāo)色

嘗試存入 gcwork 的緩存中,或全局隊(duì)列中

無鎖隊(duì)列,for 循環(huán)加原子操作實(shí)現(xiàn)棧的 push

到這里把灰色對象標(biāo)黑就完成了,又放回灰色集合接著掃下一個(gè)指針。

Go 語言設(shè)計(jì)與實(shí)現(xiàn) 垃圾收集器

Golang三色標(biāo)記+混合寫屏障GC模式全分析


分享文章:go語言博弈算法 go語言語法
URL網(wǎng)址:http://weahome.cn/article/dodcjss.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部