當前位置:成語大全網 - 書法字典 - 在Go語言中使用map時盡量不要在大地圖中保存指針。

在Go語言中使用map時盡量不要在大地圖中保存指針。

不知道大家有沒有聽過這樣壹句話:使用地圖時盡量不要在大地圖中保存指針。嗯,妳現在聽過了:)為什麽?原因是Go語言的垃圾收集器會掃描標記圖中的所有元素,GC開銷相當大,直接GG。

這兩天在Mastering Go裏,看到GC壹章比較了map和slice在垃圾收集方面的效率。書上只給出結論不說明原因,我無法忍受,於是有了這個學習筆記。說了這麽多,秀壹下妳的代碼

這是壹個簡單的測試程序。保存字符串映射的效率與保存整形映射GC的效率相差數倍。是不是有些同學會說,明明保存的是字符串,沒有指針?這是關於Go語言中string的底層實現。源代碼在src/runtime/string.go中,可以看到string實際上包含了壹個指向數據的指針和壹個長度字段。請註意這裏是否包含指針,包括基礎實現。

Go語言的GC會遞歸遍歷並標記所有可達對象,標記後清理所有未被引用的對象。當指針被掃描時,它將繼續向下看,直到結束。

Go語言中的Map是基於數組和鏈表的數據結構實現的。哈希沖突通過優化的zipper方法解決。每個桶可以保存8對鍵值,8個鍵值的數據後面有壹個溢出指針,因為壹個桶最多只能裝載8個鍵值。如果有任何額外的鍵值落入當前桶中,就需要建立另壹個桶(稱為溢出桶)並通過溢出指針鏈接它們。

由於溢出指針的原因,無論map存儲什麽,GC時都會掃描所有的bmap,這會帶來巨大的GC開銷。在《官方問題:運行時:大型映射導致顯著的GC暫停# 9477》中有關於這個問題的討論。

無腦機器翻譯如下:

如果我們有壹個map [k] v,其中k和v都不包含指針,並且我們希望提高掃描性能,我們可以執行以下操作。

添加“allOverflow [] unsafe。指針”並在其中存儲所有溢出桶。然後將bmap標記為noScan。這將使掃描非常快,因為我們不會掃描任何用戶數據。

其實會有點復雜,因為我們需要從ALLOWFLOW中刪除舊的溢出桶。而且會增加hmap的大小,所以可能需要刷新數據。

最後,官方在hmap中添加了溢出相關字段來完成上述優化,也就是具體的提交地址。

我們來看看是怎麽實現的。源代碼基於go1.15,src/cmd/compile/internal/GC/reflect . go。

從註釋中可以看出,如果保存在map中的鍵值不包含指針(由Haspointers判斷),則使用uintptr類型代替溢出字段的桶指針。在GO語言中,uintptr類型是壹個整數,其大小可以容納指針,而不是指針,這相當於將bmap標記為noScan和GC,而不遍歷整個map。隨著學習的不斷深入,我越來越覺得GO語言中的很多模塊設計的太精致了。

差不多清楚了,我能力有限。如有不妥,歡迎留言討論。源碼位置還是群裏的老大_