站長資訊網
        最全最豐富的資訊網站

        你或許聽過對Golang map做GC?

        Golang 中的 map 結構,在刪除鍵值對的時候,并不會真正的刪除,而是標記。那么隨著鍵值對越來越多,會不會造成大量內存浪費?

        首先答案是會的,很有可能導致 OOM,而且針對這個還有一個討論:github.com/golang/go/issues/20135。大致的意思就是在很大的 map 中,delete 操作沒有真正釋放內存而可能導致內存 OOM。

        所以一般的做法:就是 重建map。而 go-zero 中內置了 safemap 的容器組件。safemap 在一定程度上可以避免這種情況發生。

        那首先我們看看 go 原生提供的 map 是怎么刪除的?

        原生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)

        執行第12行的 delete,實際執行的是 runtime.mapdelete_fast64

        這些函數的參數類型是具體的 int64mapdelete_fast64 跟原始的 delete 操作一樣的,所以我們來看看 mapdelete

        mapdelete

        長圖預警!!!

        你或許聽過對Golang map做GC?

        大致代碼分析如上,具體代碼就留給大家去閱讀了。其實大致過程:

        1. 寫保護,防止并發寫
        2. 查詢要刪除的 key 是否存在
        3. 存在則對其標志做刪除標記
        4. count--

        所以你在大面積刪除 key ,實際 map 存儲的 key 是不會刪除的,只是標記當前的key狀態為 empty

        其實出發點,和 mysql 的標記刪除類似,防止后續會有相同的 key 插入,省去了擴縮容的操作。

        但是這個對有些場景是不妥的,如果開發者在未來時間內都不會再插入相同的 key ,很可能會導致 OOM

        所以針對以上情況,go-zero 開發了 safemap 。下面我們看看 safemap 是如何避免這個問題的?

        safemap

        直接從操作 safemap 中分析為什么要這么設計:

        你或許聽過對Golang map做GC?

        1. 預設一個 刪除閾值,如果觸發會放到一個新預設好的 newmap
        2. 兩個 map 是一個整體,所以 key 只能留一份

        所以為什么要設置兩個 map 就很清楚了:

        1. dirtyOld 作為存儲主體,如果 delete 操作達到閾值,則會觸發遷移。
        2. dirtyNew 作為暫存體,會在到達閾值時,存放部分 key/value

        所以在遷移操作時,我們需要做的就是:將原先的 dirtyOld 清空,存儲的 key/value 通過 for-range 重新存儲到 dirtyNew,然后將 dirtyNew 指向 dirtyOld

        可能會有疑問:不是說 key/value 沒有刪除嗎,只是標記了 tophash=empty

        其實在 for-range 過程中,會過濾掉 tophash <= emptyOne 的 key

        這樣就實現了不需要的 key 不會被加入到 dirtyNew,進而不會影響 dirtyOld

        你或許聽過對Golang map做GC?

        這其實也就是垃圾回收的年老代和新生代的概念。

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 国产成人精品怡红院在线观看| 亚洲精品视频在线看| 国产专区日韩精品欧美色| 欧洲精品色在线观看| 国产精品 综合 第五页| 国产精品天干天干综合网| 无码精品人妻一区二区三区AV| 免费视频精品一区二区三区| 亚洲精品狼友在线播放| 国内精品久久久久久久久电影网| 国产精品久久久久久| 欧美精品VIDEOSSEX少妇| 免费精品久久久久久中文字幕 | 国产精品亚洲高清一区二区| 91精品国产福利尤物| 日韩精品无码久久久久久| 午夜精品久久影院蜜桃| 国产网红主播无码精品| 92精品国产自产在线观看| 国产成人精品福利网站在线| 无码精品久久久天天影视| 亚洲第一区精品观看| 精品多毛少妇人妻AV免费久久| 91久久精品国产成人久久| 国产精品久久久久久久久免费| 国产日韩精品中文字无码 | 蜜臀精品无码AV在线播放| 久久精品国产99久久香蕉| 国产乱人伦偷精品视频| 99亚洲精品视频| 国产成人精品免费视频大全| 亚洲人成亚洲精品| 亚洲精品视频在线| 91精品国产91久久久久久| 午夜精品成年片色多多| 国产91精品在线| 久久99国产精品99久久| 日本久久久精品中文字幕| 日本精品久久久中文字幕| 99国内精品久久久久久久| 97国产视频精品|