深入理解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 相关文章推荐
goland 恢复已更改文件的操作
Apr 28 Golang
Golang之sync.Pool使用详解
May 06 Golang
解决Goland 同一个package中函数互相调用的问题
May 06 Golang
解决golang 关于全局变量的坑
May 06 Golang
Golang二维数组的使用方式
May 28 Golang
Go 语言下基于Redis分布式锁的实现方式
Jun 28 Golang
Golang 语言控制并发 Goroutine的方法
Jun 30 Golang
Golang中channel的原理解读(推荐)
Oct 16 Golang
Go语言实现一个简单的并发聊天室的项目实战
Mar 18 Golang
Go语言入门exec的基本使用
May 20 Golang
Golang gRPC HTTP协议转换示例
Jun 16 Golang
在ubuntu下安装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
做一个有下拉功能的留言版
2006/10/09 PHP
编写PHP脚本过滤用户上传的图片
2015/07/03 PHP
PHP实现动态获取函数参数的方法示例
2018/04/02 PHP
Thinkphp5.0框架视图view的循环标签用法示例
2019/10/12 PHP
JavaScript创建命名空间(namespace)的最简实现
2007/12/11 Javascript
兼容IE/Firefox/Opera/Safari的检测页面装载完毕的脚本Ext.onReady的实现
2009/07/14 Javascript
jQuery 跨域访问问题解决方法
2009/12/02 Javascript
说明你的Javascript技术很烂的五个原因
2011/04/26 Javascript
纯js简单日历实现代码
2013/10/05 Javascript
微信小程序  TLS 版本必须大于等于1.2问题解决
2017/02/22 Javascript
js学习总结_轮播图之渐隐渐现版(实例讲解)
2017/07/17 Javascript
微信小程序页面缩放式侧滑效果的实现代码
2018/11/15 Javascript
Vue项目接入Paypal实现示例详解
2020/06/04 Javascript
js实现车辆管理系统
2020/08/26 Javascript
深入理解Python中的元类(metaclass)
2015/02/14 Python
基于Python实现的ID3决策树功能示例
2018/01/02 Python
Python中if elif else及缩进的使用简述
2018/05/31 Python
python rsync服务器之间文件夹同步脚本
2019/08/29 Python
Pytorch中的VGG实现修改最后一层FC
2020/01/15 Python
Python log模块logging记录打印用法解析
2020/01/20 Python
Python HTMLTestRunner可视化报告实现过程解析
2020/04/10 Python
Python reduce函数作用及实例解析
2020/05/08 Python
详解Django中异步任务之django-celery
2020/11/05 Python
时尚的CSS3进度条效果
2012/02/22 HTML / CSS
英国女性时尚鞋类的潮流制造者:Koi Footwear
2018/10/19 全球购物
极度干燥澳大利亚官方网站:Superdry澳大利亚
2019/03/28 全球购物
新闻专业个人自我评价
2013/09/21 职场文书
大学毕业自我鉴定范文
2014/02/03 职场文书
《在山的那边》教学反思
2014/02/23 职场文书
《小池塘》教学反思
2014/02/28 职场文书
户外活动总结范文
2014/04/30 职场文书
后勤个人工作总结
2015/02/28 职场文书
新娘婚礼答谢词
2015/09/29 职场文书
小学语文教师研修日志
2015/11/13 职场文书
2016年五四青年节校园广播稿
2015/12/17 职场文书
经典《舰娘》游改全新动画预告 预定11月开播
2022/04/01 日漫