int8 , uint8
創(chuàng)新互聯(lián)公司長期為近千家客戶提供的網(wǎng)站建設服務,團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為向陽企業(yè)提供專業(yè)的成都網(wǎng)站設計、做網(wǎng)站,向陽網(wǎng)站改版等技術(shù)服務。擁有十余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
int16 , uint16
int32 , uint32
int64 , uint64
int , uint , uintptr
Go為強類型語言,所以上述類型默認為不同類型
如需運算,可通過強制類型轉(zhuǎn)換
+ , - , * , / , %
, , == , = , = 和 !=
比較運算符計算的結(jié)果是布爾值
位與: xy
位或: x|y
異或: x^y
取反: ^x
左移:
右移:
邏輯運算符的計算結(jié)果也是布爾值
邏輯與: xy
邏輯或: x||y
邏輯非: !x
不知道你有沒有聽過這么一句:在使用 map 時盡量不要在 big map 中保存指針。好吧,你現(xiàn)在已經(jīng)聽過了:)為什么呢?原因在于 Go 語言的垃圾回收器會掃描標記 map 中的所有元素,GC 開銷相當大,直接GG。
這兩天在《Mastering Go》中看到 GC 這一章節(jié)里面對比 map 和 slice 在垃圾回收中的效率對比,書中只給出結(jié)論沒有說明理由,這我是不能忍的,于是有了這篇學習筆記。扯那么多,Show Your Code
這是一個簡單的測試程序,保存字符串的 map 和 保存整形的 map GC 的效率相差幾十倍,是不是有同學會說明明保存的是 string 哪有指針?這個要說到 Go 語言中 string 的底層實現(xiàn)了,源碼在 src/runtime/string.go里,可以看到 string 其實包含一個指向數(shù)據(jù)的指針和一個長度字段。注意這里的是否包含指針,包括底層的實現(xiàn)。
Go 語言的 GC 會遞歸遍歷并標記所有可觸達的對象,標記完成之后將所有沒有引用的對象進行清理。掃描到指針就會往下接著尋找,一直到結(jié)束。
Go 語言中 map 是基于 數(shù)組和鏈表 的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的,通過 優(yōu)化的拉鏈法 解決哈希沖突,每個 bucket 可以保存 8 對鍵值,在 8 個鍵值對數(shù)據(jù)后面有一個 overflow 指針,因為桶中最多只能裝 8 個鍵值對,如果有多余的鍵值對落到了當前桶,那么就需要再構(gòu)建一個桶(稱為溢出桶),通過 overflow 指針鏈接起來。
因為 overflow 指針的緣故,所以無論 map 保存的是什么,GC 的時候就會把所有的 bmap 掃描一遍,帶來巨大的 GC 開銷。官方 issues 就有關(guān)于這個問題的討論, runtime: Large maps cause significant GC pauses #9477
無腦機翻如下:
如果我們有一個map [k] v,其中k和v都不包含指針,并且我們想提高掃描性能,則可以執(zhí)行以下操作。
將“ allOverflow [] unsafe.Pointer”添加到 hmap 并將所有溢出存儲桶存儲在其中。 然后將 bmap 標記為noScan。 這將使掃描非??欤驗槲覀儾粫呙枞魏斡脩魯?shù)據(jù)。
實際上,它將有些復雜,因為我們需要從allOverflow中刪除舊的溢出桶。 而且它還會增加 hmap 的大小,因此也可能需要重新整理數(shù)據(jù)。
最終官方在 hmap 中增加了 overflow 相關(guān)字段完成了上面的優(yōu)化,這是具體的 commit 地址。
下面看下具體是如何實現(xiàn)的,源碼基于 go1.15,src/cmd/compile/internal/gc/reflect.go 中
通過注釋可以看出,如果 map 中保存的鍵值都不包含指針(通過 Haspointers 判斷),就使用一個 uintptr 類型代替 bucket 的指針用于溢出桶 overflow 字段,uintptr 類型在 GO 語言中就是個大小可以保存得下指針的整數(shù),不是指針,就相當于實現(xiàn)了 將 bmap 標記為 noScan, GC 的時候就不會遍歷完整個 map 了。隨著不斷的學習,愈發(fā)感慨 GO 語言中很多模塊設計得太精妙了。
差不多說清楚了,能力有限,有不對的地方歡迎留言討論,源碼位置還是問的群里大佬 _
在Golang語言開發(fā)過程中,我們經(jīng)常會用到數(shù)組和切片數(shù)據(jù)結(jié)構(gòu),數(shù)組是固定長度的,而切片是可以擴張的數(shù)組,那么切片底層到底有什么不同?接下來我們來詳細分析一下內(nèi)部實現(xiàn)。
首先我們來看一下數(shù)據(jù)結(jié)構(gòu)
這里的array其實是指向切片管理的內(nèi)存塊首地址,而len就是切片的實際使用大小,cap就是切片的容量。
我們可以通過下面的代碼輸出slice:
這么分析下來,我們可以了解如下內(nèi)容:
使用一個切片通常有兩種方法:
另一種是slice = make([]int, len, cap)這種方法,稱為分配內(nèi)存。
創(chuàng)建一個slice,實質(zhì)上是在分配內(nèi)存。
這里跟一下細節(jié),math.MulUintptr是基于底層的指針計算乘法的,這樣計算不會導致超出int大小,這個方法在后面會經(jīng)常用到。
同樣,對于int64的長度,也有對應的方法
而實際分配內(nèi)存的操作調(diào)用mallocgc這個分配內(nèi)存的函數(shù),這個函數(shù)以后再分析。
我們了解切片和數(shù)組最大的不同就是切片能夠自動擴容,接下來看看切片是如何擴容的
這里可以看到,growslice是返回了一個新的slice,也就是說如果發(fā)生了擴容,會發(fā)生拷貝。
所以我們在使用過程中,如果預先知道容量,可以預先分配好容量再使用,能提高運行效率。
copy這個函數(shù)在內(nèi)部實現(xiàn)為slicecopy
還有關(guān)于字符串的拷貝
這里顯示了可以把string拷貝成[]byte,不能把[]byte拷貝成string。
1、切片的數(shù)據(jù)結(jié)構(gòu)是 array內(nèi)存地址,len長度,cap容量
2、make的時候需要注意 容量 * 長度 分配的內(nèi)存大小要小于264,并且要小于可分配的內(nèi)存量,同時長度不能大于容量。
3、內(nèi)存增長的過程:
4、當發(fā)生內(nèi)存擴容時,會發(fā)生拷貝數(shù)據(jù)的現(xiàn)象,影響程序運行的效率,如果可以,要先分配好指定的容量
5、關(guān)于拷貝,可以把string拷貝成[]byte,不能把[]byte拷貝成string。