golang 切片原理与性能优化

 

目录

一、切片原理

1.1数组与切片

1.2切片的结构

1.3切片的创建

1.4切片的重组 

1.5切片的复制

二、切片的优化

2.1空数组,nil切片和空切片

2.2切片扩容优化

2.3sync.Pool 

2.4切片复用

三、切片内存泄漏

3.1避免错误使用切片导致内存泄漏

四、其它语言中与golang切片概念类似的结构


一、切片原理

1.1数组与切片

数组是具有固定长度且拥有零个或者多个相同数据类型元素的序列。 切片是一个拥有相同类型元素的可变长度的序列。切片是“动态数组”。

数组的缺点:

1.无法添加,删除元素。

2.长度很大时,传递会耗费资源(可以传递数组指针解决)。

切片的优点:

1.不用预设长度,可以添加删除元素。

2.自动扩容。

1.2切片的结构

 

golang 切片原理与性能优化_第1张图片

运行时结构(源码位置:src/runtime/slice.go)

golang 切片原理与性能优化_第2张图片

array指针指向底层数组,len表示切片长度,cap表示切片容量。 理解起来就是,切片本身只是一个结构体,有指向数组的指针,长度和容量组成。

1.3切片的创建

golang 切片原理与性能优化_第3张图片

1.4切片的重组 

1.4.1切片重组表达式

简单表达式:a[low:high]

扩展表达式:a[low:high:max] 不常用(基本不使用)

1.4.2简单表达式

切取数组时 0<=low<=high<=len() 切取切片时 0<=low<=high<=cap()

“小坑” 一般情况下,切取都是使用底层数组,但是当low=cap时,返回空切片。即如图:

golang 切片原理与性能优化_第4张图片

 

切片扩容

Go 语言根据切片的当前容量选择不同的策略进行扩容:

如果期望容量大于当前容量的两倍就会使用期望容量;

如果当前切片的长度小于 1024 就会将容量翻倍;

如果当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量;

1.5切片的复制

 1.5.1赋值运算符

sliceA:=sliceB

 赋值运算符的复制,只是复制切片本身的结构(指向底层数组的指针,长度,容量),修改sliceA,同样会修改底层数组。

1.512copy

copyNum:=copy(destSlice,srcSlice)

 copy复制的是底层的数组,修改destSlice,不会影响srcSlice的底层数组,互不影响

 

二、切片的优化

2.1空数组,nil切片和空切片

空数组和空切片在内存中因为没有长度,指针都指向一个内存地址,可以用于强调某种特有类型。

特殊变量:zerobase

空结构体是没有内存大小的结构体。这句话是没有错的,但是更准确的来说,其实是有一个特殊起点的,那就是 zerobase 变量,这是一个 uintptr 全局变量,占用 8 个字节。当在任何地方定义无数个 struct {} 类型的变量,编译器都只是把这个 zerobase 变量的地址给出去。换句话说,在 golang 里面,涉及到所有内存 size 为 0 的内存分配,那么就是用的同一个地址 &zerobase 。

golang 切片原理与性能优化_第5张图片

 

nil切片不占用空间,切片的零值是nil。

nil切片底层数据是空,并没有指向具体地址,用来表示一个不存在的切片。所以不会分配内存,不占用空间。

2.2切片扩容优化

1.根据切片扩容规则,减少不必要的扩容。

2.根据实际需要预分配容量,尽量避免在追加过程中的扩容操作 

2.3sync.Pool 

临时对象池,增加临时对象的重用率,减少内存分配,减少GC负担。

golang 切片原理与性能优化_第6张图片

 

2.4切片复用

fasthttp中使用很多切片复用

 

 

golang 切片原理与性能优化_第7张图片

三、切片内存泄漏

3.1避免错误使用切片导致内存泄漏

返回的[]byte指向保存整个文件的数组,由于垃圾回收器不能及时释放底层数组的空间

golang 切片原理与性能优化_第8张图片

将数组赋值到新的切片

golang 切片原理与性能优化_第9张图片

四、其它语言中与golang切片概念类似的结构

4.1.python切片

相同:语法基本相同(表达式语法基本相同,如下图),概念相似,都是指原数据的一部分。

不同:

1.底层结构不同。python切片的数据都是原数组的一个副本(是值)。不会对原数组有影响。而golang切片返回的只是一个由指向底层数组的指针,切片长度,切片容量组成的结构体,本身没有值。对切片进行操作会影响底层数组。

2.golang切片的API较少,比如想删除一个元素较复杂,且在数组头部添加会增加内存消耗。

4.2.c++的vector容器

扩容策略相似,大小,容量概念也相似。

参考文章:

1.https://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651444342&idx=4&sn=db4cc84f21562a36725602eff1cb853a&chksm=80bb0a84b7cc8392a74ab81b0e830c4903febded17f3c81581c6f74597b716ea9b4fbdcb05c3&scene=132#wechat_redirect

你可能感兴趣的:(go,golang)