深入理解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 相关文章推荐
Go缓冲channel和非缓冲channel的区别说明
Apr 25 Golang
go语言-在mac下brew升级golang
Apr 25 Golang
golang在GRPC中设置client的超时时间
Apr 27 Golang
Golang之sync.Pool使用详解
May 06 Golang
Golang中异常处理机制详解
Jun 08 Golang
Go语言基础知识点介绍
Jul 04 Golang
简单聊聊Golang中defer预计算参数
Mar 25 Golang
victoriaMetrics库布隆过滤器初始化及使用详解
Apr 05 Golang
Golang获取List列表元素的四种方式
Apr 20 Golang
Golang bufio详细讲解
Apr 21 Golang
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
Jun 21 Golang
Go语言编译原理之源码调试
Aug 05 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
smarty模板嵌套之include与fetch性能测试
2010/12/05 PHP
基于PHP生成静态页的实现方法
2013/05/10 PHP
PHP自动生成后台导航网址的最佳方法
2013/08/27 PHP
CI框架开发新浪微博登录接口源码完整版
2014/05/28 PHP
PHP中多维数组的foreach遍历示例
2014/06/13 PHP
php实现微信扫码支付
2017/03/26 PHP
关于 byval 与 byref 的区别分析总结
2007/10/08 Javascript
JavaScript 学习笔记(十一)
2010/01/19 Javascript
两个多选select(multiple左右)添加、删除选项和取值实例
2014/05/12 Javascript
jQuery插件scroll实现无缝滚动效果
2015/04/27 Javascript
详解WordPress开发中get_current_screen()函数的使用
2016/01/11 Javascript
javascript中获取元素标签中间的内容的实现方法
2016/10/08 Javascript
基于vue2的canvas时钟倒计时组件步骤解析
2018/11/05 Javascript
Javascript三种字符串连接方式及性能比较
2019/05/28 Javascript
js获取浏览器地址(获取第1个斜杠后的内容)
2019/09/03 Javascript
JS错误处理与调试操作实例分析
2020/04/13 Javascript
python socket网络编程步骤详解(socket套接字使用)
2013/12/06 Python
python中迭代器(iterator)用法实例分析
2015/04/29 Python
Python模块包中__init__.py文件功能分析
2016/06/14 Python
Python冒泡排序注意要点实例详解
2016/09/09 Python
用Python将IP地址在整型和字符串之间轻松转换
2017/03/22 Python
Python进阶_关于命名空间与作用域(详解)
2017/05/29 Python
使用python将大量数据导出到Excel中的小技巧分享
2018/06/14 Python
python 利用pandas将arff文件转csv文件的方法
2019/02/12 Python
python将字符串转变成dict格式的实现
2019/11/18 Python
关于windows下Tensorflow和pytorch安装教程
2020/02/04 Python
python误差棒图errorbar()函数实例解析
2020/02/11 Python
浅谈Python 函数式编程
2020/06/20 Python
GAP美国官网:美国休闲时尚品牌
2016/08/26 全球购物
戴森比利时官方网站:Dyson BE
2020/10/03 全球购物
机电专业大学生职业规划书范文
2014/02/25 职场文书
和睦家庭事迹
2014/05/14 职场文书
团拜会策划方案
2014/06/07 职场文书
党员转正申请报告
2015/05/15 职场文书
团结友爱主题班会
2015/08/13 职场文书
Python上下文管理器Content Manager
2021/06/26 Python