深入理解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 27 Golang
golang通过递归遍历生成树状结构的操作
Apr 28 Golang
golang 如何用反射reflect操作结构体
Apr 28 Golang
Go语言设计模式之结构型模式
Jun 22 Golang
Golang 语言控制并发 Goroutine的方法
Jun 30 Golang
Go 语言结构实例分析
Jul 04 Golang
golang 语言中错误处理机制
Aug 30 Golang
Go 语言中 20 个占位符的整理
Oct 16 Golang
Go语言基础map用法及示例详解
Nov 17 Golang
golang为什么要统一错误处理
Apr 03 Golang
Golang实现可重入锁的示例代码
May 25 Golang
Golang gRPC HTTP协议转换示例
Jun 16 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
PHP提示Notice: Undefined variable的解决办法
2012/11/24 PHP
利用phpexcel把excel导入数据库和数据库导出excel实现
2014/01/09 PHP
PHP中round()函数对浮点数进行四舍五入的方法
2014/11/19 PHP
浅谈php处理后端&接口访问超时的解决方法
2016/10/29 PHP
比较简单实用的使用正则三种版本的js去空格处理方法
2007/11/18 Javascript
实现变速回到顶部的JavaScript代码
2011/05/09 Javascript
js简单实现HTML标签Select联动带跳转
2013/10/23 Javascript
JS实现鼠标单击与双击事件共存
2014/03/08 Javascript
jQuery鼠标事件汇总
2015/08/30 Javascript
jquery实现通用的内容渐显Tab选项卡效果
2015/09/07 Javascript
js阻止浏览器默认行为的简单实例
2016/05/15 Javascript
AngularJS入门之动画
2016/07/27 Javascript
Angularjs结合Bootstrap制作的一个TODO List
2016/08/18 Javascript
JavaScript实现HTML5游戏断线自动重连的方法
2017/09/18 Javascript
JS常见DOM节点操作示例【创建 ,插入,删除,复制,查找】
2018/05/14 Javascript
微信JS-SDK实现微信会员卡功能(给用户微信卡包里发送会员卡)
2019/07/25 Javascript
[28:57]EG vs VGJ.T 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/16 DOTA
python支持断点续传的多线程下载示例
2014/01/16 Python
详解python进行mp3格式判断
2016/12/23 Python
通过cmd进入python的实例操作
2019/06/26 Python
python绘制直方图和密度图的实例
2019/07/08 Python
python中Lambda表达式详解
2019/11/20 Python
查看端口并杀进程python脚本代码
2019/12/17 Python
python实现从尾到头打印单链表操作示例
2020/02/22 Python
Scotch Porter官方网站:男士美容产品
2020/08/31 全球购物
澳大利亚排名第一的露营和户外设备在线零售商:Outbax
2020/05/06 全球购物
保险专业大专生求职信
2013/10/26 职场文书
我的小天地教学反思
2014/04/30 职场文书
工作岗位说明书模板
2014/05/09 职场文书
诉前财产保全担保书
2014/05/20 职场文书
学校端午节活动方案
2014/08/23 职场文书
2014年银行年终工作总结
2014/12/19 职场文书
师德承诺书
2015/01/20 职场文书
企业催款函范本
2015/06/24 职场文书
初中政治教学工作总结
2015/08/13 职场文书
如何利用python实现列表嵌套字典取值
2022/06/10 Python