此链接:http://research.swtch.com/godata
它说(切片的第三段):
因为切片是多字结构而不是指针,所以切片操作不需要分配内存,甚至切片头也不需要分配内存,切片头通常可以保留在堆栈上.这种表示使切片与在C中传递显式指针和长度对一样便宜.最初将切片表示为指向上述结构的指针,但这样做意味着每个切片操作都分配了一个新的内存对象.即使使用快速分配器,也会为垃圾收集器创建大量不必要的工作,并且我们发现,与上面的字符串一样,程序避免了切片操作,转而使用显式索引.删除间接和分配使得切片足够便宜,以避免在大多数情况下传递显式索引.
什么...?为什么不分配任何内存?如果是多字结构或指针?它不需要分配内存吗?然后它提到它最初是指向该切片结构的指针,它需要为新对象分配内存.为什么现在不需要这样做?非常困惑
切片操作不需要分配内存.
"切片操作"是指s1[x:y]切片初始化或不切片的事情make([]int, x).例如:
var s1 = []int{0, 1, 2, 3, 4, 5} // <<- allocates (or put on stack)
s2 := s1[1:3] // <<- does not (normally) allocate
Run Code Online (Sandbox Code Playgroud)
也就是说,第二行类似于:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
…
example := SliceHeader{&s1[1], 2, 5}
Run Code Online (Sandbox Code Playgroud)
通常局部变量就像example放入堆栈一样.就像这样做而不是使用结构一样:
var exampleData uintptr
var exampleLen, exampleCap int
Run Code Online (Sandbox Code Playgroud)
这些example*变量进入堆栈.只有当代码执行return &example或otherFunc(&example)以其他方式允许指向此的指针转义时,编译器才会强制在堆上分配结构(或切片头).
然后它提到它最初是指向该切片结构的指针,它需要为新对象分配内存.为什么现在不需要这样做?
想象一下,你没有做过以上的事情:
example2 := &SliceHeader{…same…}
// or
example3 := new(SliceHeader)
example3.Data = …
example3.Len = …
example3.Cap = …
Run Code Online (Sandbox Code Playgroud)
即类型*SliceHeader而不是SliceHeader.根据你提到的内容,这实际上就是切片(pre Go 1.0).
它也曾经是两者example2并且example3必须在堆上分配.这就是所谓的"新对象的记忆".我认为现在转义分析会尝试将这两个放在堆栈上,只要指针保持在函数的本地,这样它就不再那么大了.无论哪种方式,避免一个级别的间接是好的,与复制指针和重复解除引用相比,复制三个整数几乎总是更快.