深入理解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 正则匹配效率详解
Apr 25 Golang
go 原生http web 服务跨域restful api的写法介绍
Apr 27 Golang
Golang 使用Map实现去重与set的功能操作
Apr 29 Golang
go语言中fallthrough的用法说明
May 06 Golang
Go 自定义package包设置与导入操作
May 06 Golang
K8s部署发布Golang应用程序的实现方法
Jul 16 Golang
Go 通过结构struct实现接口interface的问题
Oct 05 Golang
golang中的struct操作
Nov 11 Golang
如何利用golang运用mysql数据库
Mar 13 Golang
golang为什么要统一错误处理
Apr 03 Golang
Go 内联优化让程序员爱不释手
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
PHP4引用文件语句的对比
2006/10/09 PHP
PHP新手上路(十二)
2006/10/09 PHP
PHP微信红包API接口
2015/12/05 PHP
PHP符合PSR编程规范的实例分享
2016/12/21 PHP
PHP二维索引数组的遍历实例分析【2种方式】
2019/06/24 PHP
laravel 修改.htaccess文件 重定向public的解决方法
2019/10/12 PHP
js数组Array sort方法使用深入分析
2013/02/21 Javascript
原生JS操作网页给p元素添加onclick事件及表格隔行变色
2013/12/01 Javascript
js如何调用qq互联api实现第三方登录
2014/03/28 Javascript
PHP使用方法重载实现动态创建属性的get和set方法
2014/11/17 Javascript
Javascript实现可旋转的圆圈实例代码
2015/08/04 Javascript
js事件驱动机制 浏览器兼容处理方法
2016/07/23 Javascript
JavaScript实现简单的日历效果
2016/09/25 Javascript
Vue.js第四天学习笔记(组件)
2016/12/02 Javascript
探讨跨域请求资源的几种方式(总结)
2016/12/02 Javascript
Vue.js展示AJAX数据简单示例讲解
2017/03/29 Javascript
浅谈用Webpack路径压缩图片上传尺寸获取的问题
2018/02/22 Javascript
浅谈vue 锚点指令v-anchor的使用
2019/11/13 Javascript
实例讲解Python的函数闭包使用中应注意的问题
2016/06/20 Python
TensorFlow 实战之实现卷积神经网络的实例讲解
2018/02/26 Python
python/sympy求解矩阵方程的方法
2018/11/08 Python
python selenium 查找隐藏元素 自动播放视频功能
2019/07/24 Python
解决pycharm同一目录下无法import其他文件
2020/02/12 Python
通过实例解析Python RPC实现原理及方法
2020/07/07 Python
详解python 内存优化
2020/08/17 Python
QT5 Designer 打不开的问题及解决方法
2020/08/20 Python
行政文员岗位职责
2013/11/08 职场文书
大学活动总结格式
2014/04/29 职场文书
关于责任的演讲稿
2014/05/20 职场文书
节约粮食标语
2014/06/18 职场文书
教师国庆节演讲稿范文2014
2014/09/21 职场文书
索赔员岗位职责
2015/02/15 职场文书
志愿服务心得体会
2016/01/15 职场文书
2016年学校党支部创先争优活动总结
2016/04/05 职场文书
深入浅出讲解Java8函数式编程
2022/01/18 Java/Android
win10音频服务未响应怎么解决?win10音频服务未响应未修复的解决方法
2022/08/14 数码科技