go slice内存分配

内存结构

map和slice类型都是通过make方法和new方法来初始化。使用make初始化时,会同时分配空间,使用new初始化时,不会分配空间,指向的是一个nil。传递一个map变量时,实际上是传递了上面提到的分配hmap结构体的地址,可以理解为传递了引用。通过打印结果也可以反应这个机制。new出来的slice可以赋值(append)操作.

首先对于 slice 结构进行一个简单的了解 结构定义 slice对应的runtime 包的相关源码参见: https://golang.org/src/runtime/slice.go

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

var slice []int 定义的变量内部结构如下:

slice.array = nil
slice.len = 0
slice.cap = 0    // 所以多次append,回触发自动扩容和内存拷贝

如果我们声明了一下变量 slice := []int{} 或 slice := make([]int, 0) 的内部结构如下:

slice.array = 0xxxxxxxx  // 分配了地址
slice.len = 0
slice.cap = 18208800

如果使用 make([]byte, 5) 定义的话,结构如下图:

如果使用 s := s[2:4],则结构如下图:

一个例子:

func main() {
   var s []int
   for i := 1; i <= 3; i++ {
      s = append(s, i)
   }
   fmt.Printf("before reverse the length: %d  slice: %v \n",len(s),s)
   reverse(s)
   fmt.Printf("after reverese the length: %d  slice: %v \n",len(s),s)
}

func reverse(s []int) {
   s = append(s, 999)
 
   for i, j := 0, len(s)-1; i < j; i++ {
      j = len(s) - (i + 1)
      s[i], s[j] = s[j], s[i]
   }
}

结论:

1.slice作为函数参数时,他的数据可能会改变,但是属性例如长度、容量却不会变。
2. slice作为参数时,拷贝的是指针,在没有扩容的时候,仍然与原来的slice指向相同的内存,利用拷贝指针对内存数据的修改会影响原来的slice。但是长度和容量并不会改变。
3. 涉及到拷贝指针扩容时,拷贝指针和原来slice指向的内存不一样,拥有不同的内存块,操作彼此独立。
4. 用make(type,0,capacity)来创建slice。

为何不用动态链表实现slice?

  • 首先拷贝一断连续的内存是很快的,假如不想发生拷贝,也就是用动态链表,那你就没有连续内存。此时随机访问开销会是:链表 O(N), 2倍增长块链 O(LogN),二级表一个常数很大的O(1)。问题不仅是算法上开销,还有内存位置分散而对缓存高度不友好,这些问题i在连续内存方案里都是不存在的。除非你的应用是狂append然后只顺序读一次,否则优化写而牺牲读都完全不 make sense. 而就算你的应用是严格顺序读,缓存命中率也通常会让你的综合效率比拷贝换连续内存低。

  • 对小 slice 来说,连续 append 的开销更多的不是在 memmove, 而是在分配一块新空间的 memory allocator 和之后的 gc 压力(这方面对链表更是不利)。所以,当你能大致知道所需的最大空间(在大部分时候都是的)时,在make的时候预留相应的 cap 就好。如果所需的最大空间很大而每次使用的空间量分布不确定,那你就要在浪费内存和耗 CPU 在 allocator + gc 上做权衡。

  • Go 在 append 和 copy 方面的开销是可预知+可控的,应用上简单的调优有很好的效果。这个世界上没有免费的动态增长内存,各种实现方案都有设计权衡。

什么时候该用slice?

在go语言中slice是很灵活的,大部分情况都能表现的很好,但也有特殊情况。
当程序要求slice的容量超大并且需要频繁的更改slice的内容时,就不应该用slice,改用list更合适。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

anssummer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值