golang slice 原始碼解讀

2022-05-18 19:45:09 字數 3683 閱讀 7981

本文從原始碼角度學習 golang slice 的建立、擴容,深拷貝的實現。

slice 僅有三個字段,其中array 是儲存資料的部分,len 欄位為長度,cap 為容量。

type slice struct
通過下面**可以輸出空slice 的大小:

package main

import "fmt"

import "unsafe"

func main()

建立乙個slice,其實就是分配記憶體。cap, len 的設定在彙編中完成。

下面的**主要是做了容量大小的判斷,以及記憶體的分配。

func makeslice(et *_type, len, cap int) unsafe.pointer 

panicmakeslicecap()

} // 分配記憶體

// 小物件從當前p 的cache中空閒資料中分配

// 大的物件 (size > 32kb) 直接從heap中分配

// runtime/malloc.go

return mallocgc(mem, et, true)

}

對於不需要記憶體擴容的slice,直接資料拷貝即可。

上面的dx 存放的就是array 指標,ax 是資料的偏移. 將 123 存入陣列。

而對於容量不夠的情況,就需要對slice 進行擴容。這也是slice 比較關心的地方。 (因為對於大slice,grow slice會影響到記憶體的分配和執行的效率)

func growslice(et *_type, old slice, cap int) slice 

// 如果儲存的型別空間為0, 比如說 struct{}, 資料為空,長度不為空

if et.size == 0

} newcap := old.cap

doublecap := newcap + newcap

if cap > doublecap else else

// 通過校驗newcap 大於0檢查容量是否溢位。

if newcap <= 0

}} var overflow bool

var lenmem, newlenmem, capmem uintptr

// 為了加速計算(少用除法,乘法)

// 對於不同的slice元素大小,選擇不同的計算方法

// 獲取需要申請的記憶體大小。

switch else

lenmem = uintptr(old.len) << shift

newlenmem = uintptr(cap) << shift

capmem = roundupsize(uintptr(newcap) << shift)

overflow = uintptr(newcap) > (maxalloc >> shift)

newcap = int(capmem >> shift)

default:

// 其他用除法

lenmem = uintptr(old.len) * et.size

newlenmem = uintptr(cap) * et.size

capmem, overflow = math.muluintptr(et.size, uintptr(newcap))

capmem = roundupsize(capmem)

newcap = int(capmem / et.size)

} // 判斷是否會溢位

if overflow || capmem > maxalloc

// 記憶體分配

var p unsafe.pointer

if et.kind&kindnopointers != 0 else

} // 資料拷貝

memmove(p, old.array, lenmem)

return slice

}

切片的淺拷貝
shallowcopy := data[:1]

ptr1 := unsafe.pointer(&shallowcopy)

opt1 := (*[3]int)(ptr1)

fmt.println(opt1[0])

下面是上述**的彙編**:

上面,先將 data 的成員資料拷貝到暫存器,然後從暫存器拷貝到shallowcopy的物件中。(注意到只是拷貝了指標而已, 所以是淺拷貝)

切片的深拷貝

深拷貝也比較簡單,只是做了一次記憶體的深拷貝。

func slicecopy(to, fm slice, width uintptr) int 

n := fm.len

if to.len < n

// 元素大小為0,則直接返回

if width == 0

// 竟態分析和記憶體掃瞄

// ...

size := uintptr(n) * width

// 直接記憶體拷貝

if size == 1 else

return n

}// 字串slice的拷貝

func slicestringcopy(to byte, fm string) int

n := len(fm)

if len(to) < n

// 竟態分析和記憶體掃瞄

// ...

memmove(unsafe.pointer(&to[0]), stringstructof(&fm).str, uintptr(n))

return n

}

彙編的生成方法

go tool compile -n -s slice.go > slice.s
需要了解unsafe.pointer 的使用

slice.go 位於 runtime/slice.go

上述**使用 go1.12.5 版本

還有一點需要提醒, type 長度為0的物件。比如說 struct{} 型別。(所以,很多使用chan struct{} 做channel 的傳遞,節省記憶體)

azkaban web server原始碼解析

azkaban主要用於hadoop相關job任務的排程,但也可以應用任何需要排程管理的任務,可以完全代替crontab。azkaban主要分為web server 任務上傳,管理,排程 executor server 接受web server的排程指令,進行任務執行 1.資料表 projects 工...

JDK LinkedHashMap原始碼解析

今天來分析一下jdk linkedhashmap的源 public class linkedhashmapextends hashmapimplements map可以看到,linkedhashmap繼承自hashmap,並且也實現了map介面,所以linkedhashmap沿用了hashmap的大...

Redux原始碼createStore解讀常用方法

const store createstore reducer,preloadedstate enhancer 直接返回當前currentstate,獲取state值,return state 我覺得應該深轉殖乙個新的物件返回,不然有可能會被外部修改 function getstate consol...