golang 中的切片不分配任何内存?

Slices in golang do not allocate any memory?

这个link:http://research.swtch.com/godata

它说(切片部分的第三段):

Because slices are multiword structures, not pointers, the slicing operation does not need to allocate memory, not even for the slice header, which can usually be kept on the stack. This representation makes slices about as cheap to use as passing around explicit pointer and length pairs in C. Go originally represented a slice as a pointer to the structure shown above, but doing so meant that every slice operation allocated a new memory object. Even with a fast allocator, that creates a lot of unnecessary work for the garbage collector, and we found that, as was the case with strings above, programs avoided slicing operations in favor of passing explicit indices. Removing the indirection and the allocation made slices cheap enough to avoid passing explicit indices in most cases.

什么...?为什么它不分配任何内存?如果它是多字结构或指针?它不需要分配内存吗?然后它提到它本来是指向那个slice结构的指针,它需要为一个新的对象分配内存。为什么现在不需要这样做?很困惑

每种数据类型在初始化时都会分配内存。在博客中,他清楚地提到

the slicing operation does not need to allocate memory.

他是对的。现在看,how slice works in golang

Slices hold references to an underlying array, and if you assign one slice to another, both refer to the same array. If a function takes a slice argument, changes it makes to the elements of the slice will be visible to the caller, analogous to passing a pointer to the underlying array.

展开

the slicing operation does not need to allocate memory.

"Slicing operation" 指的是 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

即第二行相似

type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
}
…
example := SliceHeader{&s1[1], 2, 5}

通常像 example 这样的局部变量会被放入堆栈。就像这样做而不是使用结构一样:

var exampleData            uintptr
var exampleLen, exampleCap int

那些 example* 变量进入堆栈。 仅当代码执行 return &exampleotherFunc(&example) 或以其他方式允许指向 this 的指针转义时,编译器才会被迫在堆上分配结构(或切片头)。

Then it mentions that it was originally a pointer to that slice structure, and it needed to allocate memory for a new object. Why does it not need to do that now?

想象一下,你没有做上面的事情:

example2 := &SliceHeader{…same…}
// or
example3 := new(SliceHeader)
example3.Data = …
example3.Len = …
example3.Cap = …

即类型是 *SliceHeader 而不是 SliceHeader。 根据您提到的内容,这实际上是 slice 过去的样子(Go 1.0 之前)。

以前也是example2example3都得分配到堆上。那就是所指的 "memory for a new object" 。我 认为 现在逃逸分析将尝试将这两个都放到堆栈上,只要指针保持在函数的本地,所以它不再是一个大问题。不管怎样,避免一级间接寻址是好的,与复制指针并重复取消引用相比,复制三个整数几乎总是更快。