深入理解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:垃圾回收GC触发条件详解
Apr 24 Golang
golang中实现给gif、png、jpeg图片添加文字水印
Apr 26 Golang
go 原生http web 服务跨域restful api的写法介绍
Apr 27 Golang
解决Golang time.Parse和time.Format的时区问题
Apr 29 Golang
Golang表示枚举类型的详细讲解
Sep 04 Golang
Go归并排序算法的实现方法
Apr 06 Golang
Golang流模式之grpc的四种数据流
Apr 13 Golang
golang连接MySQl使用sqlx库
Apr 14 Golang
Golang 对es的操作实例
Apr 20 Golang
深入理解 Golang 的字符串
May 04 Golang
GO中sync包自由控制并发示例详解
Aug 05 Golang
基于Python实现西西成语接龙小助手
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
10 个经典PHP函数
2013/10/17 PHP
php简单计算权重的方法示例【适合抽奖类应用】
2019/06/10 PHP
PHP实现简单用户登录界面
2019/10/23 PHP
laravel框架模型中非静态方法也能静态调用的原理分析
2019/11/23 PHP
用JS剩余字数计算的代码
2008/07/03 Javascript
一个选择最快的服务器转向代码
2009/04/27 Javascript
JavaScript基础知识之数据类型
2012/08/06 Javascript
Firefox中使用outerHTML的2种解决方法
2014/06/07 Javascript
Javascript基础教程之break和continue语句
2015/01/18 Javascript
JavaScript操作Oracle数据库示例
2015/03/06 Javascript
基于jquery实现百度新闻导航菜单滑动动画
2016/03/15 Javascript
JavaScript检测原始值、引用值、属性
2016/06/20 Javascript
JS严格模式知识点总结
2018/02/27 Javascript
vue单页面实现当前页面刷新或跳转时提示保存
2018/11/02 Javascript
vue-router实现嵌套路由的讲解
2019/01/19 Javascript
JavaScript中的一些实用小技巧总结
2019/04/07 Javascript
微信小程序的开发范式BeautyWe.js入门详解
2019/07/10 Javascript
浅谈layui框架自带分页和表格重载的接口解析问题
2019/09/11 Javascript
详解vue-router 动态路由下子页面多页共活的解决方案
2019/12/22 Javascript
react中hook介绍以及使用教程
2020/12/11 Javascript
Python类的基础入门知识
2008/11/24 Python
Python2.x中str与unicode相关问题的解决方法
2015/03/30 Python
Python中尝试多线程编程的一个简明例子
2015/04/07 Python
实践Python的爬虫框架Scrapy来抓取豆瓣电影TOP250
2016/01/20 Python
python递归查询菜单并转换成json实例
2017/03/27 Python
Ubuntu16.04/树莓派Python3+opencv配置教程(分享)
2018/04/02 Python
Python 3.7新功能之dataclass装饰器详解
2018/04/21 Python
python tkinter界面居中显示的方法
2018/10/11 Python
django之导入并执行自定义的函数模块图解
2020/04/01 Python
HTML5事件方法全部汇总
2016/05/12 HTML / CSS
澳大利亚波希米亚风时尚品牌:Tree of Life
2019/09/15 全球购物
什么是静态路由?什么是动态路由?各自的特点是什么?
2015/09/16 面试题
乡镇信息公开实施方案
2014/03/23 职场文书
会计专业求职信
2014/08/10 职场文书
如何理解及使用Python闭包
2021/06/01 Python
Python制作一个随机抽奖小工具的实现
2021/07/07 Python