深入理解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 08 Golang
Golang 正则匹配效率详解
Apr 25 Golang
golang中实现给gif、png、jpeg图片添加文字水印
Apr 26 Golang
Golang中interface{}转为数组的操作
Apr 30 Golang
golang elasticsearch Client的使用详解
May 05 Golang
浅谈golang package中init方法的多处定义及运行顺序问题
May 06 Golang
Golang的继承模拟实例
Jun 30 Golang
详解Go语言Slice作为函数参数的使用
Jul 02 Golang
Go语言基础知识点介绍
Jul 04 Golang
Golang获取List列表元素的四种方式
Apr 20 Golang
Go Grpc Gateway兼容HTTP协议文档自动生成网关
Jun 16 Golang
Go语言测试库testify使用学习
Jul 23 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
大师制作的中短波矿石收音机
2020/04/02 无线电
CI框架中$this->load->library()用法分析
2016/05/18 PHP
php实现的双色球算法示例
2017/06/20 PHP
PHP实现通过strace定位故障原因的方法
2018/04/29 PHP
PHP convert_uudecode()函数讲解
2019/02/14 PHP
详解PHP的抽象类和抽象方法以及接口总结
2019/03/15 PHP
文本框只能选择数据到文本框禁止手动输入
2013/11/22 Javascript
jQuery中DOM操作实例分析
2015/01/23 Javascript
解决vue 更改计算属性后select选中值不更改的问题
2018/03/02 Javascript
解决vue A对象赋值给B对象,修改B属性会影响到A的问题
2018/09/25 Javascript
Python读写及备份oracle数据库操作示例
2018/05/17 Python
Linux下python3.7.0安装教程
2018/07/30 Python
Django如何自定义分页
2018/09/25 Python
更改Python的pip install 默认安装依赖路径方法详解
2018/10/27 Python
django如何自己创建一个中间件
2019/07/24 Python
Flask教程之重定向与错误处理实例分析
2019/08/01 Python
Python 词典(Dict) 加载与保存示例
2019/12/06 Python
tensorflow 模型权重导出实例
2020/01/24 Python
Tensorflow 多线程设置方式
2020/02/06 Python
使用canvas绘制贝塞尔曲线
2014/12/17 HTML / CSS
HTML5页面音视频在微信和app下自动播放的实现方法
2016/10/20 HTML / CSS
韩国三大免税店之一:THE GRAND 中文免税店
2016/07/21 全球购物
卡骆驰新加坡官网:Crocs新加坡
2018/06/12 全球购物
ABOUT YOU匈牙利:500个最受欢迎的时尚品牌
2019/07/19 全球购物
初婚初育证明
2014/01/14 职场文书
学年末自我鉴定
2014/01/21 职场文书
大学生职业生涯规划范文——找准自我,定位人生
2014/01/23 职场文书
应用英语专业自荐信
2014/01/26 职场文书
廉洁自律演讲稿
2014/05/22 职场文书
村创先争优活动总结
2014/08/28 职场文书
2014年师德师风自我剖析材料
2014/09/27 职场文书
场地使用证明模板
2014/10/25 职场文书
医院科室评语
2015/01/04 职场文书
2015年设计师个人工作总结
2015/04/25 职场文书
浅谈MySQL之select优化方案
2021/08/07 MySQL
MybatisPlus EntityWrapper如何自定义SQL
2022/03/22 Java/Android