深入理解go slice结构


Posted in Golang onSeptember 15, 2021

文章

slice介绍

append的机制

slice tricks

go data

slice

array的语法: [4]int{1,2,3,4}, [...]int{1,2,3}。在go中array是值类型,这就意味着一个类型为array的变量名并不是一个指针,当传递值是,array总是被复制。

slice的语法: []int{1,2,3,4}, make([]int), make([]int,10)

当make只有两个参数时,cap和len相同。

slice本质上是array的一个片段的描述,它包含3部分:
[ptr, len, cap]

通过make([]int, 5)创建的slice,其内存布局如下:

深入理解go slice结构

对这个slice进行截断之后,形成新的slice:

深入理解go slice结构

使用cap可以对slice进行扩增: s[:cap(s)].

copy(dst, src []T) int的用法: copy会复制dst和src中长度最小的元素所对应的数量(所以如果dst==nil, 则copy返回0)。并且,copy还能处理dst和src存在重叠的情况。
用法:

t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t

append(dst []T, element...T) []T的实现,与下面的AppendByte类似:
首先检查容量,容量不足则使用make构造一个新的slice并将原来的元素移动。

func AppendByte(slice []byte, data ...byte) []byte {
    m := len(slice)
    n := m + len(data)
    if n > cap(slice) { // if necessary, reallocate
        // allocate double what's needed, for future growth.
        newSlice := make([]byte, (n+1)*2)
        copy(newSlice, slice)
        slice = newSlice
    }
    slice = slice[0:n]
    copy(slice[m:n], data)
    return slice
}

slice对gc的影响(gotcha)

如果对一个很大的数组,取其中很小的一段切片,都会造成这个数组不会被gc回收。
gc使用mark-and-sweep(标记清除)。gc维护一个已分配的堆对象表,在标记阶段,它将寄存器,堆栈上的指针作为root进行遍历标记。

为什么部分slice会导致整体的array不会回收呢?设想下面的slice:

a := [4]int{0,1,2,3}
s := a[1:2] // {1}
return s

a会不会被回收呢?答案是不会,因为gc遍历s时,通过data指针找到对应的array切片,它发现这个地址在之前分配的一个array对象的范围内,从而标记这个array为可到达对象,避免其被整个清除。(这里所要理解的就是array是按范围标记的,并不是按指针头标记的,因为一个内存块对象是有范围的,如果被部分引用,说明整个对象仍然是可达的。)

如何解决?如果是这种情况,一个较大array返回较小切片,可以使用复制:

before:

var digitRegexp = regexp.MustCompile("[0-9]+")

func FindDigits(filename string) []byte {
    b, _ := ioutil.ReadFile(filename)
    return digitRegexp.Find(b)
}

after:

func CopyDigits(filename string) []byte {
    b, _ := ioutil.ReadFile(filename)
    b = digitRegexp.Find(b)
    c := make([]byte, len(b))
    copy(c, b)
    return c
}

reflect.SliceHeader

package reflect

// SliceHeader和StringHeader具有相同的Data和Len,这导致[]byte可以直接转换成string,而不需要任何复制
type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}
type StringHeader struct {
	Data uintptr
	Len  int
}

转换代码:

func SliceByteAsString(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}
func StringAsSliceByte(s string) []byte {
    p := unsafe.Pointer(&d)
    var b []byte
    hdr :=  (*reflect.SliceHeader)(unsafe.Pointer(&b))
    hdr.Data = uintptr(p)
    hdr.Cap = len(s)
    hdr.Len = len(s)
    return b
}

func Int64AsByteSlice(d int64) []byte {
    p := unsafe.Pointer(&d)
    var b []byte
    hdr :=  (*reflect.SliceHeader)(unsafe.Pointer(&b))
    hdr.Data = uintptr(p)
    hdr.Cap = 8
    hdr.Len = 8
    return b
}

到此这篇关于go slice结构的文章就介绍到这了,更多相关go slice结构内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Golang 相关文章推荐
golang DNS服务器的简单实现操作
Apr 30 Golang
go mod 安装依赖 unkown revision问题的解决方案
May 06 Golang
基于Golang 高并发问题的解决方案
May 08 Golang
关于golang高并发的实现与注意事项说明
May 08 Golang
go xorm框架的使用
May 22 Golang
go 实现简易端口扫描的示例
May 22 Golang
修改并编译golang源码的操作步骤
Jul 25 Golang
Go语言实现一个简单的并发聊天室的项目实战
Mar 18 Golang
Golang jwt身份认证
Apr 20 Golang
Golang bufio详细讲解
Apr 21 Golang
Golang gRPC HTTP协议转换示例
Jun 16 Golang
GoFrame框架数据校验之校验结果Error接口对象
Jun 21 Golang
Golang表示枚举类型的详细讲解
golang 语言中错误处理机制
Aug 30 #Golang
Golang并发操作中常见的读写锁详析
Aug 30 #Golang
Go中的条件语句Switch示例详解
Aug 23 #Golang
Go Plugins插件的实现方式
Aug 07 #Golang
使用GO语言实现Mysql数据库CURD的简单示例
Aug 07 #Golang
go使用Gin框架利用阿里云实现短信验证码功能
Aug 04 #Golang
You might like
FCKeditor的安装(PHP)
2007/01/13 PHP
Yii清理缓存的方法
2016/01/06 PHP
用PHP写的一个冒泡排序法的函数简单实例
2016/05/26 PHP
Laravel 5.5 的自定义验证对象/类示例代码详解
2017/08/29 PHP
phpStudy配置多站点多域名方法及遇到的403错误解决方法
2017/10/19 PHP
PHP 数组黑名单/白名单实例代码详解
2019/06/04 PHP
TP5框架实现的数据库备份功能示例
2020/04/05 PHP
一个JavaScript继承的实现
2006/10/24 Javascript
基于jquery的仿百度搜索框效果代码
2011/04/11 Javascript
jQuery EasyUI API 中文文档 - Tabs标签页/选项卡
2011/10/01 Javascript
js函数调用的方式
2014/05/06 Javascript
js实现点击左右按钮轮播图片效果实例
2015/01/29 Javascript
angularjs客户端实现压缩图片文件并上传实例
2015/07/06 Javascript
IE8下jQuery改变png图片透明度时出现的黑边
2015/08/30 Javascript
莱鸟介绍window.print()方法
2016/01/06 Javascript
总结Javascript中数组各种去重的方法
2016/10/04 Javascript
puppeteer实现html截图的示例代码
2019/01/10 Javascript
echarts统计x轴区间的数值实例代码详解
2019/07/07 Javascript
vue使用i18n实现国际化的方法详解
2019/09/05 Javascript
解决vue做详情页跳转的时候使用created方法 数据不会更新问题
2020/07/24 Javascript
python实现人脸识别经典算法(一) 特征脸法
2018/03/13 Python
python中找出numpy array数组的最值及其索引方法
2018/04/17 Python
对Python中type打开文件的方式介绍
2018/04/28 Python
基于anaconda下强大的conda命令介绍
2018/06/11 Python
Python把csv数据写入list和字典类型的变量脚本方法
2018/06/15 Python
python调用百度语音REST API
2018/08/30 Python
Django框架教程之中间件MiddleWare浅析
2019/12/29 Python
Python selenium键盘鼠标事件实现过程详解
2020/07/28 Python
python中用ctypes模拟点击的实例讲解
2020/11/26 Python
纽约和芝加哥当天送花:Ode à la Rose
2019/07/05 全球购物
出纳试用期自我鉴定
2014/04/07 职场文书
餐饮投资计划书
2014/04/25 职场文书
2015年销售部工作总结范文
2015/04/27 职场文书
董事会决议范本
2015/07/01 职场文书
情感电台广播稿
2015/08/18 职场文书
php去除deprecated的实例方法
2021/11/17 PHP