這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
上一篇提到的關於gc效能的問題,對比slice和map的結構可以看出為了儲存資料map用了更多的記憶體空間,並且可能存在鏈表,鏈表的每個節點在gc時都做為一個小對象對待,增加了掃描的時間,因此gc時間相對更長。
slice初始化與複製
slice通過內部指標和相關屬性引用數組片段,來實現變長方案。實現方式和資料結構都類似C++中的vector。它本身是結構體,作為參數傳遞時傳遞的是slice本身而不是它引用的底層數組。len()可獲得slice長度,cap()可獲得slice容量。
type slice struct {array unsafe.Pointerlen intcap int}
slice可以通過數組初始化,也可以直接make。make時直接使用cap作為new的長度來建立底層數組,返回的是slice結構體。如果通過new([]int)
來初始化,它返回的是一個指向slice結構體的指標,不能直接對它進行下標操作。
func makeslice(t *slicetype, len64, cap64 int64) slice {p := newarray(t.elem, uintptr(cap))return slice{p, len, cap}}
遍曆slice時經常用到range操作,range會複製range的對象。下面例子中在迴圈內部改變slice的屬性,最終會作用到slice上導致最後輸出[1 2 101]
。但是並不會導致迴圈在第三次就結束,因為range s是從s的複本中讀取i和n的。s的複本只複製了指標,底層元素仍指向同一片,因此可以在迴圈內改變slice元素的值並在迴圈期內可見。
func main() {s := []int{1, 2, 3, 4, 5}for i, n := range s {if i == 0 {s = s[:3]s[2] = n + 100}fmt.Println(i, n) // 輸出1 2;2 101;3 4;4 5}fmt.Println(s)//輸出 1 2 101}
reslice擴容
reslice的增長規則:如果新的size是當前size的2倍以上,則大小增長為新size。如果新的size不到當前size的2倍,則按當前size的不同有不同操作。當前size不超過1024,按每次2倍增長,否則按當前大小的1/4增長。
slice通過append元素使得元素達到cap,就會重新分配記憶體,複製內容並接著append,即便指向的數組還有空位。比如這個例子a初始化為長度和容量都是3的slice,再往a中append資料時a將在堆上重新分配空間並複製原始內容,因此這時原始數組的後幾位已經看不到了。
func main() {data := [6]int{0, 1, 2, 3, 4, 5}a := data[:3] a = append(a, 100) // output [0 1 2 100]}
如果slice作為函數的入參,通常希望對slice的操作可以影響到底層資料,但是如果在函數內部append資料超過了cap,導致重新分配底層數組,這時入參a指向的底層數組跟調用方實參指向的不再是同一個。如下面的例子這樣因為擴容導致與代碼實現原意相違背,因此通常不建議在函數內部對slice有append操作,若有需要則顯示的返回這個slice。
func main() {a := []int{1} // afeter initialization len=1 cap=1test(a) // call test to append slice, but a is [1], not [1 2]}func test(a []int) { a = append(a, 2) }
string記憶體分布和複製
string的結構和C++STL實現的string類似。都是由指向固定地址的str指標和len組成的結構體。對string的複製只是對指標和len的複製,作為函數參數時入參只不過是指向同一個底層資料的相同指標。
通常string常量是編譯器分配到唯讀段的(.rodata),對應的資料地址不可寫入。fmt.Sprintf
產生的字串分配在堆上,對應資料地址可修改。
struct string { byte* str; intgo len;}
string與[]byte轉化
平常使用中經常將兩者互相轉化,每次相互轉化時都會發生底層資料的複製。如果是動態產生的字串可以通過以下對指標的操作來直接轉化資料,而不需要拷貝,效能好接近4倍。
//return GoString's buffer slice(enable modify string)func StringBytes(s string) Bytes { return *(*Bytes)(unsafe.Pointer(&s))}// convert b to string without copyfunc BytesString(b []byte) String { return *(*String)(unsafe.Pointer(&b))}
參考部落格:
http://blog.csdn.net/vipally/article/details/52940119
https://studygolang.com/articles/2909