這篇文章主要為大家展示了“如何給Golang map做GC”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“如何給Golang map做GC”這篇文章吧。
創(chuàng)新互聯(lián)建站長期為上千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為婺源企業(yè)提供專業(yè)的網(wǎng)站設(shè)計制作、成都網(wǎng)站制作,婺源網(wǎng)站改版等技術(shù)服務(wù)。擁有十載豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
在 Golang中的 map 結(jié)構(gòu),在刪除鍵值對的時候,并不會真正的刪除,而是標(biāo)記。那么隨著鍵值對越來越多,會不會造成大量內(nèi)存浪費?
首先答案是會的,很有可能導(dǎo)致 OOM,而且針對這個還有一個討論:github.com/golang/go/issues/20135。大致的意思就是在很大的 map
中,delete
操作沒有真正釋放內(nèi)存而可能導(dǎo)致內(nèi)存 OOM。
所以一般的做法:就是 重建map。而 go-zero
中內(nèi)置了 safemap
的容器組件。safemap
在一定程度上可以避免這種情況發(fā)生。
那首先我們看看 go
原生提供的 map
是怎么刪除的?
1 package main 2 3 func main() { 4 m := make(map[int]string, 9) 5 m[1] = "hello" 6 m[2] = "world" 7 m[3] = "go" 8 9 v, ok := m[1] 10 _, _ = fn(v, ok) 11 12 delete(m, 1) 13 } 14 15 func fn(v string, ok bool) (string, bool) { 16 return v, ok 17 }
測試代碼如上,我們可以通過 go tool compile -S -N -l testmap.go | grep "CALL"
:
0x0071 00113 (test/testmap.go:4) CALL runtime.makemap(SB) 0x0099 00153 (test/testmap.go:5) CALL runtime.mapassign_fast64(SB) 0x00ea 00234 (test/testmap.go:6) CALL runtime.mapassign_fast64(SB) 0x013b 00315 (test/testmap.go:7) CALL runtime.mapassign_fast64(SB) 0x0194 00404 (test/testmap.go:9) CALL runtime.mapaccess2_fast64(SB) 0x01f1 00497 (test/testmap.go:10) CALL "".fn(SB) 0x0214 00532 (test/testmap.go:12) CALL runtime.mapdelete_fast64(SB) 0x0230 00560 (test/testmap.go:7) CALL runtime.gcWriteBarrier(SB) 0x0241 00577 (test/testmap.go:6) CALL runtime.gcWriteBarrier(SB) 0x0252 00594 (test/testmap.go:5) CALL runtime.gcWriteBarrier(SB) 0x025c 00604 (test/testmap.go:3) CALL runtime.morestack_noctxt(SB)
執(zhí)行第12行的 delete
,實際執(zhí)行的是 runtime.mapdelete_fast64
。
這些函數(shù)的參數(shù)類型是具體的 int64
,mapdelete_fast64
跟原始的 delete
操作一樣的,所以我們來看看 mapdelete
。
長圖預(yù)警?。?!
大致代碼分析如上,具體代碼就留給大家去閱讀了。其實大致過程:
寫保護(hù),防止并發(fā)寫
查詢要刪除的 key
是否存在
存在則對其標(biāo)志做刪除標(biāo)記
count--
所以你在大面積刪除 key
,實際 map
存儲的 key
是不會刪除的,只是標(biāo)記當(dāng)前的key狀態(tài)為 empty
。
其實出發(fā)點,和 MySQL
的標(biāo)記刪除類似,防止后續(xù)會有相同的 key
插入,省去了擴縮容的操作。
但是這個對有些場景是不妥的,如果開發(fā)者在未來時間內(nèi)都不會再插入相同的 key
,很可能會導(dǎo)致 OOM
。
所以針對以上情況,go-zero
開發(fā)了 safemap
。下面我們看看 safemap
是如何避免這個問題的?
直接從操作 safemap
中分析為什么要這么設(shè)計:
預(yù)設(shè)一個 刪除閾值,如果觸發(fā)會放到一個新預(yù)設(shè)好的 newmap
中
兩個 map
是一個整體,所以 key
只能留一份
所以為什么要設(shè)置兩個 map
就很清楚了:
dirtyOld
作為存儲主體,如果 delete
操作達(dá)到閾值,則會觸發(fā)遷移。
dirtyNew
作為暫存體,會在到達(dá)閾值時,存放部分 key/value
所以在遷移操作時,我們需要做的就是:將原先的 dirtyOld
清空,存儲的 key/value 通過 for-range 重新存儲到 dirtyNew
,然后將 dirtyNew
指向 dirtyOld
。
可能會有疑問:不是說
key/value
沒有刪除嗎,只是標(biāo)記了tophash=empty
其實在
for-range
過程中,會過濾掉tophash <= emptyOne
的 key
這樣就實現(xiàn)了不需要的 key 不會被加入到 dirtyNew
,進(jìn)而不會影響 dirtyOld
。
這其實也就是垃圾回收的年老代和新生代的概念。
以上是“如何給Golang map做GC”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
網(wǎng)站名稱:如何給Golangmap做GC
轉(zhuǎn)載來源:http://www.muchs.cn/article6/ghgcog.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、外貿(mào)建站、網(wǎng)站維護(hù)、靜態(tài)網(wǎng)站、App開發(fā)、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)