Go Slice與String記憶體布局和實現

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

上一篇提到的關於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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.