Golang 切片附加和重新分配
Golang slice append and reallocation
我最近一直在学习围棋,对发生重新分配时切片的行为有疑问。假设我有一片指向结构的指针,例如:
var a []*A
如果我要将这个切片传递给另一个函数,我的理解是在内部这按值传递一个切片 header,它在一个单独的 goroutine 上运行并且只从切片中读取,而函数启动 goroutine 继续附加到切片,这是一个问题吗?例如:
package main
type A struct {
foo int
}
func main() {
a := make([]*A, 0, 100)
ch := make(chan int)
for i := 0; i < 100; i++ {
a = append(a, &A{i})
}
go read_slice(a, ch)
for i := 0; i < 100; i++ {
a = append(a, &A{i+100})
}
<-ch
}
func read_slice(a []*A, ch chan int) {
for i := range a {
fmt.Printf("%d ", a[i].foo)
}
ch <- 1
}
所以根据我的理解,由于 read_slice() 函数是 运行 在它自己的 goroutine 上,带有切片的副本 header,它有一个指向当前的底层指针后备数组和调用时的大小,我可以通过它访问 foo。
但是,当另一个 goroutine 附加到 slice 时,它会在超过容量时触发重新分配。 go 运行时是否不会将内存释放到 read_slice() 中使用的旧支持数组,因为在该函数中有对它的引用?
我用 "go run -race slice.go" 试过 运行 这个,但没有报告任何东西,但我觉得我可能在这里做错了什么?任何指针将不胜感激。
谢谢!
在没有对支持数组的引用之前,GC 不会收集支持数组。节目中没有比赛。
考虑没有 goroutines 的场景:
a := make([]*A, 0, 100)
for i := 0; i < 100; i++ {
a = append(a, &A{i})
}
b := a
for i := 0; i < 100; i++ {
b = append(b, &A{i+100})
}
当 append to b
分配新的支持数组时,切片 a
将继续使用前 100 个指针引用支持数组。切片 a
没有留下对后备数组的悬空引用。
现在将 goroutine 添加到场景中:
a := make([]*A, 0, 100)
for i := 0; i < 100; i++ {
a = append(a, &A{i})
}
b := a
go read_slice(a, ch)
for i := 0; i < 100; i++ {
b = append(b, &A{i+100})
}
goroutine可以愉快的使用slicea
。没有悬挂的参考。
现在考虑问题中的程序。它在功能上与此处的最后一个片段相同。
我最近一直在学习围棋,对发生重新分配时切片的行为有疑问。假设我有一片指向结构的指针,例如:
var a []*A
如果我要将这个切片传递给另一个函数,我的理解是在内部这按值传递一个切片 header,它在一个单独的 goroutine 上运行并且只从切片中读取,而函数启动 goroutine 继续附加到切片,这是一个问题吗?例如:
package main
type A struct {
foo int
}
func main() {
a := make([]*A, 0, 100)
ch := make(chan int)
for i := 0; i < 100; i++ {
a = append(a, &A{i})
}
go read_slice(a, ch)
for i := 0; i < 100; i++ {
a = append(a, &A{i+100})
}
<-ch
}
func read_slice(a []*A, ch chan int) {
for i := range a {
fmt.Printf("%d ", a[i].foo)
}
ch <- 1
}
所以根据我的理解,由于 read_slice() 函数是 运行 在它自己的 goroutine 上,带有切片的副本 header,它有一个指向当前的底层指针后备数组和调用时的大小,我可以通过它访问 foo。
但是,当另一个 goroutine 附加到 slice 时,它会在超过容量时触发重新分配。 go 运行时是否不会将内存释放到 read_slice() 中使用的旧支持数组,因为在该函数中有对它的引用?
我用 "go run -race slice.go" 试过 运行 这个,但没有报告任何东西,但我觉得我可能在这里做错了什么?任何指针将不胜感激。
谢谢!
在没有对支持数组的引用之前,GC 不会收集支持数组。节目中没有比赛。
考虑没有 goroutines 的场景:
a := make([]*A, 0, 100)
for i := 0; i < 100; i++ {
a = append(a, &A{i})
}
b := a
for i := 0; i < 100; i++ {
b = append(b, &A{i+100})
}
当 append to b
分配新的支持数组时,切片 a
将继续使用前 100 个指针引用支持数组。切片 a
没有留下对后备数组的悬空引用。
现在将 goroutine 添加到场景中:
a := make([]*A, 0, 100)
for i := 0; i < 100; i++ {
a = append(a, &A{i})
}
b := a
go read_slice(a, ch)
for i := 0; i < 100; i++ {
b = append(b, &A{i+100})
}
goroutine可以愉快的使用slicea
。没有悬挂的参考。
现在考虑问题中的程序。它在功能上与此处的最后一个片段相同。