詳解golang的切片擴(kuò)容機(jī)制
前言
golang的擴(kuò)容機(jī)制:在go1.18之前有一個臨界值為1024,小于1024的時候,切片先兩倍擴(kuò)容,如果兩倍擴(kuò)容后的容量還是不夠,就直接以切片需要的容量作為容量。
在go1.18之后,臨界值換成了256,小于256和前面相同,大于256公式變?yōu)椋╫ldcap+3*256)/4這個公式的值隨著oldcap的越來越大,從2一直接近1.25,相對于1.18之前可以更平滑的過渡。
上面就是切片擴(kuò)容的基本規(guī)則機(jī)制,但還有一個小的規(guī)則,下面就來看看
發(fā)現(xiàn)問題
看下面一行代碼
運(yùn)行結(jié)果如下
出現(xiàn)上面的結(jié)果原因是,剛開始切片容量為1,兩倍擴(kuò)容之后,變?yōu)?,但是2不夠,所以變?yōu)樗枰娜萘?,后面在加4個數(shù)字,繼續(xù)擴(kuò)容,2倍之后是6,但是6不夠應(yīng)該變?yōu)樾枰娜萘浚瑧?yīng)該為7,為什么是8呢
解決問題
先貼一下源代碼,下面第一段代碼是擴(kuò)容機(jī)制的基礎(chǔ)代碼(1.18之前)
下面是造成上面原因的代碼
下面是1.18之后的基礎(chǔ)擴(kuò)容機(jī)制
從上面代碼分析之后,造成7變成8的原因就在這個roundupsize函數(shù)上面,它有一個計算公式
capmem = roundupsize(uintptr(newcap) * ptrSize) newcap = int(capmem / ptrSize)
其中 ptrSize
在 64 位機(jī)器下的大小為 8。 而此時 newcap
的值為 7,所以傳入到 roundupsize
函數(shù)內(nèi)部的值為 7 * 8 = 56
。接著看看 roundupsize
的內(nèi)部:
func roundupsize(size uintptr) uintptr { if size < _MaxSmallSize { if size <= smallSizeMax-8 { return uintptr(class_to_size[size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]]) } else { //…… } } //…… }
const _MaxSmallSize = 32768 const smallSizeMax = 1024 const smallSizeDiv = 8 var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31} var class_to_size = [_NumSizeClasses]uint16{0, 8, 16,,24,32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768}
roundupsize
的返回值為 uintptr(class_to_size[size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]])
,而:
- (size+smallSizeDiv-1)/smallSizeDiv = (56 + 8 - 1) / 8 = 7
- size_to_class8[7] = 5
- class_to_size[5] = 64 所以
roundopsize
的返回值為 64 ,newcap = int(capmem / ptrSize) = int(64 / 8) = 8
,所以最終原切片的容量擴(kuò)充到了 8。
總結(jié)
如果以后遭遇情景題我們按照這個公式算的話很費(fèi)時間,所以我總結(jié)了一下規(guī)則,就是如果遇見2倍擴(kuò)容之后不夠需要直接用它所需要的容量的情況,如果是1和3,它的容量可以是1和3,但如果是5,7,9,11等其他的奇數(shù),全部取+1得到離他最近的偶數(shù),因?yàn)檫@個值與class_to_size有關(guān),class_to_size全部存的是8的倍數(shù),把class_to_size里面的值全部除以8,結(jié)果為:0,1,2,3,4,6,8,10...........和咱們的規(guī)律非常吻合
到此這篇關(guān)于詳解golang的切片擴(kuò)容機(jī)制的文章就介紹到這了,更多相關(guān)golang切片擴(kuò)容機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用gin開發(fā)的golang項(xiàng)目三種開發(fā)模式方式
這篇文章主要介紹了用gin開發(fā)的golang項(xiàng)目三種開發(fā)模式方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01golang利用redis和gin實(shí)現(xiàn)保存登錄狀態(tài)校驗(yàn)登錄功能
這篇文章主要介紹了golang利用redis和gin實(shí)現(xiàn)保存登錄狀態(tài)校驗(yàn)登錄功能,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-01-01Go?數(shù)據(jù)結(jié)構(gòu)之二叉樹詳情
這篇文章主要介紹了?Go?數(shù)據(jù)結(jié)構(gòu)之二叉樹詳情,二叉樹是一種數(shù)據(jù)結(jié)構(gòu),在每個節(jié)點(diǎn)下面最多存在兩個其他節(jié)點(diǎn)。即一個節(jié)點(diǎn)要么連接至一個、兩個節(jié)點(diǎn)或不連接其他節(jié)點(diǎn),下文基于GO語言展開二叉樹結(jié)構(gòu)詳情,需要的朋友可以參考一下2022-05-05Go+Redis緩存設(shè)計與優(yōu)化實(shí)現(xiàn)
本文主要介紹了Go+Redis緩存設(shè)計與優(yōu)化實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02深入探討Golang中如何進(jìn)行并發(fā)發(fā)送HTTP請求
在?Golang?領(lǐng)域,并發(fā)發(fā)送?HTTP?請求是優(yōu)化?Web?應(yīng)用程序的一項(xiàng)重要技能,本文探討了實(shí)現(xiàn)此目的的各種方法,文中的示例代碼講解詳細(xì),希望對大家有所幫助2024-01-01golang多次讀取http request body的問題分析
這篇文章主要給大家分析了golang多次讀取http request body的問題,文中通過代碼示例和圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-01-01