本文從原始碼角度學習 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...