深入理解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中切片copy复制和等号复制的区别介绍
Apr 27 Golang
go语言中json数据的读取和写出操作
Apr 28 Golang
Go 实现英尺和米的简单单位换算方式
Apr 29 Golang
go设置多个GOPATH的方式
May 05 Golang
golang switch语句的灵活写法介绍
May 06 Golang
Golang标准库syscall详解(什么是系统调用)
May 25 Golang
Golang生成Excel文档的方法步骤
Jun 09 Golang
golang fmt格式“占位符”的实例用法详解
Jul 04 Golang
golang实现一个简单的websocket聊天室功能
Oct 05 Golang
golang中的struct操作
Nov 11 Golang
Go语言特点及基本数据类型使用详解
Mar 21 Golang
Go归并排序算法的实现方法
Apr 06 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
新浪新闻小偷
2006/10/09 PHP
PHP变量内存分配问题记录整理
2013/11/27 PHP
PHP用反撇号执行外部命令
2015/04/14 PHP
php反射类ReflectionClass用法分析
2016/05/12 PHP
js表格分页实现代码
2009/09/18 Javascript
jQuery 扩展对input的一些操作方法
2009/10/30 Javascript
cnblogs csdn 代码运行框实现代码
2009/11/02 Javascript
JQuery+DIV自定义滚动条样式的具体实现
2013/06/25 Javascript
解析JavaScript中的不可见数据类型
2013/12/02 Javascript
理解JavaScript表单的基础知识
2016/01/25 Javascript
JS简单设置下拉选择框默认值的方法
2016/08/20 Javascript
详解Angular的内置过滤器和自定义过滤器【推荐】
2016/12/26 Javascript
JS中将多个逗号替换为一个逗号的实现代码
2017/06/23 Javascript
解决angular双向绑定无效果,ng-model不能正常显示的问题
2018/10/02 Javascript
vue-cli 项目打包完成后运行文件路径报错问题
2019/07/19 Javascript
Javascript操作select控件代码实例
2020/02/14 Javascript
实现vuex原理的示例
2020/10/21 Javascript
[38:21]2018DOTA2亚洲邀请赛3月30日 小组赛A组 LGD VS Newbee
2018/03/31 DOTA
Python 匹配任意字符(包括换行符)的正则表达式写法
2009/10/29 Python
python安装mysql-python简明笔记(ubuntu环境)
2016/06/25 Python
python正则实现计算器功能
2017/12/14 Python
Python操作mongodb的9个步骤
2018/06/04 Python
详解重置Django migration的常见方式
2019/02/15 Python
python函数声明和调用定义及原理详解
2019/12/02 Python
Python装饰器原理与基本用法分析
2020/01/07 Python
opencv 实现特定颜色线条提取与定位操作
2020/06/02 Python
美国当红的名品折扣网:Gilt Groupe
2016/08/15 全球购物
澳大利亚在线百货商店:Real Smart
2017/08/13 全球购物
白血病捐款倡议书
2014/05/14 职场文书
民主评议党员工作总结
2014/10/20 职场文书
2015新教师教学工作总结
2015/07/22 职场文书
2016年校园社会综合治理宣传月活动总结
2016/03/16 职场文书
写作技巧:怎样写好一份优秀工作总结?
2019/08/14 职场文书
5人制售《绝地求生》游戏外挂获利500多万元 被判刑
2022/03/31 其他游戏
微信小程序 根据不同用户切换不同TabBar
2022/04/21 Javascript