在內存中的形式
首先看一下在go中,一些基礎類型在內存中是以什么形態存在的,如下圖所示:
變量j的類型是int32, 而變量i的類型是int,兩者不是同一個類型,所以賦值操作i=j
是一種類型錯誤cannot use j (type int32) as type int in assignment
。
正確的方式應該是
i := int(7) j := int32(7) i = int(j)
結構體的域在內存中是緊密排列的。
靜態類型和底層類型
byte是Go的靜態類型,uint8是Go的底層類型
rune是int32的別名,用於表示unicode字符。通常在處理中文的時候需要用到它
string類型
定義在go-1.8.3/go/src/runtime/string.go
type stringStruct struct { str unsafe.Pointer len int }
兩個屬性:一個指針,一個int型的長度,都是私有成員!
string類型類型在Go語言的內存模型中用一個2字長的數據結構表示。 從上圖可以看出,其實多個string是共享一個存儲的。
str[i:j]
進行字符串切片操作,會得到一個新的type stringStruct struct
對象,該對象的指針依然還是指向str的底層存儲,長度為j-i
。 這說明字符串切分不涉及內存分配或復制操作,其效率等同於傳遞下標。
內建函數len()對string類型的操作是直接從底層結構中取出len值,而不需要額外的操作
slice類型
定義在/go-1.8.3/src/runtime/slice.go
type slice struct { array unsafe.Pointer len int cap int }
顯然,type slice struct和上面的type stringStruct struct很類似,只是多了一個cap屬性。
一個slice是一個底層數組的部分引用。同理,對底層數據進行切片操作也不會涉及到內存分配或復制操作,僅僅是新建了一個type slice struct
對象而已!
需要注意的是,在上圖中,y[0:4]是有效的,打印出來的結果會是[3,5,7,11]
由於slice是不同於指針的多字長結構,分割操作並不需要分配內存,甚至沒有通常被保存在堆中的slice頭部。這種表示方法使slice操作和在C中傳遞指針、長度對一樣廉價。
slice相關的函數有如下幾個,是不是感覺很熟悉。
func makeslice(et *_type, len64, cap64 int64) slice func growslice(et *_type, old slice, cap int) slice func slicecopy(to, fm slice, width uintptr) int func slicestringcopy(to []byte, fm string) int
slice的擴容
對slice進行append操作時,可能會造成擴容操作。擴容規則如下:
- 如果新的大小是當前大小2倍以上,則大小增長為新大小
- 否則循環以下操作:如果當前長度len小於1024,按每次2倍增長,否則每次按當前cap的1/4增長。直到增長的大小超過或等於新大小。
newcap := old.cap doublecap := newcap + newcap //和old.cap的double進行比較 if cap > doublecap { newcap = cap } else { if old.len < 1024 { newcap = doublecap } else { for newcap < cap { newcap += newcap / 4 } } }
slice與unsafe.Pointer相互轉換
- 利用make+slice弄塊內存出來自己管理。。。。這個比較牛逼了
s := make([]byte, 200) ptr := unsafe.Pointer(&s[0])
- 基於內存指針ptr構造出一個slice
var o []byte sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&o))) sliceHeader.Cap = length sliceHeader.Len = length sliceHeader.Data = uintptr(ptr)
new和make
了解完Go對type slice struct
的定義之后,再來理解new和make的差異就簡單得多了。
- new(T),僅分配內存,不進行初始化。返回的
*T
指向一個類型T的零值。 - make(T, args),分配內存,且進行初始化。返回是
T
本身。因為T本身就是一個引用類型。
以下屬聲明的類型為例子,分別用new和make的效果如下圖:
type Point struct { X, Y int } type Rect1 struct { Min, Max Point } type Rect2 struct { Min, Max *Point }
原文: https://github.com/Kevin-fqh/learning-k8s-source-code/blob/master/%E6%B7%B1%E5%85%A5go/(01)%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B.md