深入理解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语言map与string的相互转换的实现
Apr 07 Golang
golang中切片copy复制和等号复制的区别介绍
Apr 27 Golang
解决Golang中ResponseWriter的一个坑
Apr 27 Golang
解决Goland 同一个package中函数互相调用的问题
May 06 Golang
GoLang中生成UUID唯一标识的实现
May 08 Golang
Golang生成Excel文档的方法步骤
Jun 09 Golang
Go语言读取txt文档的操作方法
Jan 22 Golang
如何利用golang运用mysql数据库
Mar 13 Golang
浅谈GO中的Channel以及死锁的造成
Mar 18 Golang
golang实现浏览器导出excel文件功能
Mar 25 Golang
Go归并排序算法的实现方法
Apr 06 Golang
Golang 实现 WebSockets 之创建 WebSockets
Apr 24 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 字符串加密函数(在指定时间内加密还原字符串,超时无法还原)
2010/04/28 PHP
PHP框架Swoole定时器Timer特性分析
2014/08/19 PHP
摘自织梦CMS中的图片处理类
2015/08/08 PHP
修改Laravel自带的认证系统的User类的命名空间的步骤
2019/10/15 PHP
php上传后台无法收到数据解决方法
2019/10/28 PHP
jquery 得到当前页面高度和宽度的两个函数
2010/02/21 Javascript
Javascript实现真实字符串剩余字数提示的实例代码
2013/10/22 Javascript
如何在JavaScript中实现私有属性的写类方式(二)
2013/12/04 Javascript
javascript生成大小写字母
2015/07/03 Javascript
JavaScript数据类型学习笔记分享
2016/09/01 Javascript
微信小程序之picker日期和时间选择器
2017/02/09 Javascript
详解如何使用Vue2做服务端渲染
2017/03/29 Javascript
jQuery实现的文字逐行向上间歇滚动效果示例
2017/09/06 jQuery
vue axios请求超时的正确处理方法
2018/04/02 Javascript
Webpack devServer中的 proxy 实现跨域的解决
2018/06/15 Javascript
解决vue中使用Axios调用接口时出现的ie数据处理问题
2018/08/13 Javascript
JavaScript栈和队列相关操作与实现方法详解
2018/12/07 Javascript
详解vue-cli3 中跨域解决方案
2019/04/10 Javascript
node.js实现带进度条的多文件上传
2020/03/27 Javascript
[02:02:38]VG vs Mineski Supermajor 败者组 BO3 第一场 6.6
2018/06/07 DOTA
多线程爬虫批量下载pcgame图片url 保存为xml的实现代码
2013/01/17 Python
详解Python中映射类型(字典)操作符的概念和使用
2015/08/19 Python
python使用xslt提取网页数据的方法
2018/02/23 Python
Python实现输出某区间范围内全部素数的方法
2018/05/02 Python
基于Python中求和函数sum的用法详解
2018/06/28 Python
python微元法计算函数曲线长度的方法
2018/11/08 Python
Python使用sax模块解析XML文件示例
2019/04/04 Python
Python中面向对象你应该知道的一下知识
2019/07/10 Python
利用python实现汉诺塔游戏
2021/03/01 Python
给同事的道歉信
2014/01/11 职场文书
物理分数没达标检讨书
2014/09/13 职场文书
格林童话读书笔记
2015/06/30 职场文书
技能培训通讯稿
2015/07/18 职场文书
2016春季运动会前导词
2015/11/25 职场文书
HR在给员工开具离职证明时,需要注意哪些问题?
2019/07/03 职场文书
读《庄子》有感:美而不自知
2019/11/06 职场文书