Golang Slice and string reuse

Source: Internet
Author: User

A major improvement over C/c++,golang is the introduction of a GC mechanism that eliminates the need for users to manage their own memory, significantly reducing the bugs introduced by the program due to memory leaks, but at the same time the GC introduces additional performance overhead, sometimes even due to improper use, which makes GC a performance bottleneck, so Golang programming, special attention should be paid to the reuse of objects to reduce the pressure of GC. While slice and string are basic types of golang, understanding the internal mechanisms of these basic types helps us to better reuse these objects

Slice and string internal structure

The internal structure of the slice and string can be $GOROOT/src/reflect/value.go found inside

type StringHeader struct {    Data uintptr    Len  int}type SliceHeader struct {    Data uintptr    Len  int    Cap  int}

You can see that a string contains a data pointer and a length, and the length is immutable

The slice contains a data pointer, a length and a capacity, and when the capacity is insufficient, it will re-request new memory, the data pointer will point to the new address, the original address space will be freed

From these structures, it can be seen that the assignment of string and slice, including as a parameter pass, and a custom struct, are just shallow copies of the Data pointer.

Slice Reuse

Append operation

si1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}si2 := si1si2 = append(si2, 0)Convey("重新分配内存", func() {    header1 := (*reflect.SliceHeader)(unsafe.Pointer(&si1))    header2 := (*reflect.SliceHeader)(unsafe.Pointer(&si2))    fmt.Println(header1.Data)    fmt.Println(header2.Data)    So(header1.Data, ShouldNotEqual, header2.Data)})

SI1 and Si2 begin to point to the same array, and when the append operation is performed on the Si2, because the original cap value is not enough, the Data value is changed, and there is a $GOROOT/src/reflect/value.go policy on the new CAP value in this file, where grow function, when the cap is less than 1024, it is multiplied, over the time, each increase of 25%, and this memory growth is not only the data copy (from the old address copy to the new address) need to consume extra performance, the release of old address memory will also cause additional burden to the GC, So if you can know the length of the data case, try to use make([]int, len, cap) pre-allocated memory, do not know the length of the case, you can consider the following memory reuse method

Memory Reuse

si1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}si2 := si1[:7]Convey("不重新分配内存", func() {    header1 := (*reflect.SliceHeader)(unsafe.Pointer(&si1))    header2 := (*reflect.SliceHeader)(unsafe.Pointer(&si2))    fmt.Println(header1.Data)    fmt.Println(header2.Data)    So(header1.Data, ShouldEqual, header2.Data)})Convey("往切片里面 append 一个值", func() {    si2 = append(si2, 10)    Convey("改变了原 slice 的值", func() {        header1 := (*reflect.SliceHeader)(unsafe.Pointer(&si1))        header2 := (*reflect.SliceHeader)(unsafe.Pointer(&si2))        fmt.Println(header1.Data)        fmt.Println(header2.Data)        So(header1.Data, ShouldEqual, header2.Data)        So(si1[7], ShouldEqual, 10)    })})

Si2 is a slice of SI1, from the first code can see the slices do not reallocate memory, SI2 and SI1 Data pointer to the same address, and the second code can see that when we si2 a new value in the Append, we found that there is still no memory allocation, and this operation So that the value of the SI1 also changed, because the two are pointing to the same piece of Data area, using this feature, we just have to si1 = si1[:0] be able to continuously empty the contents of SI1, to achieve memory reuse

PS: You can use to copy(si2, si1) implement deep copy

String

Convey("字符串常量", func() {    str1 := "hello world"    str2 := "hello world"    Convey("地址相同", func() {        header1 := (*reflect.StringHeader)(unsafe.Pointer(&str1))        header2 := (*reflect.StringHeader)(unsafe.Pointer(&str2))        fmt.Println(header1.Data)        fmt.Println(header2.Data)        So(header1.Data, ShouldEqual, header2.Data)    })})

This example is simple, string constants use the same address area

Convey("相同字符串的不同子串", func() {    str1 := "hello world"[:6]    str2 := "hello world"[:5]    Convey("地址相同", func() {        header1 := (*reflect.StringHeader)(unsafe.Pointer(&str1))        header2 := (*reflect.StringHeader)(unsafe.Pointer(&str2))        fmt.Println(header1.Data, str1)        fmt.Println(header2.Data, str2)        So(str1, ShouldNotEqual, str2)        So(header1.Data, ShouldEqual, header2.Data)    })})

Different substrings of the same string, no additional requests for new memory, but note that the same string here, refers str1.Data == str2.Data && str1.Len == str2.Len to, rather than str1 == str2 , the following example can be explained str1 == str2 but its Data is not the same

Convey("不同字符串的相同子串", func() {    str1 := "hello world"[:5]    str2 := "hello golang"[:5]    Convey("地址不同", func() {        header1 := (*reflect.StringHeader)(unsafe.Pointer(&str1))        header2 := (*reflect.StringHeader)(unsafe.Pointer(&str2))        fmt.Println(header1.Data, str1)        fmt.Println(header2.Data, str2)        So(str1, ShouldEqual, str2)        So(header1.Data, ShouldNotEqual, header2.Data)    })})

In fact, for a string, you just have to remember that the string is immutable, no strings are requested for extra memory (for internal data pointers only), I've been smart about designing a cache to store strings to reduce the space occupied by repeating strings, in fact, Unless the string itself is created by itself, []byte the string itself is a substring of another string (such as strings.Split a string obtained) and would not have applied for additional space, which is superfluous

Reference links

    • Go Slices:usage and Internals:https://blog.golang.org/go-slices-usage-and-internals
    • Test Code Link: https://github.com/hatlonely/hellogolang/blob/master/internal/buildin/reuse_test.go
thumb_up

Reprint Please indicate the source
This article links:http://www.hatlonely.com/2018/03/17/golang-slice-and-string-reuse/

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.