深入理解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 Gin实现文件上传下载的示例代码
Apr 02 Golang
Go缓冲channel和非缓冲channel的区别说明
Apr 25 Golang
golang中实现给gif、png、jpeg图片添加文字水印
Apr 26 Golang
Golang 使用Map实现去重与set的功能操作
Apr 29 Golang
golang switch语句的灵活写法介绍
May 06 Golang
golang 实现并发求和
May 08 Golang
Golang全局变量加锁的问题解决
May 08 Golang
go xorm框架的使用
May 22 Golang
Go语言基础切片的创建及初始化示例详解
Nov 17 Golang
Golang流模式之grpc的四种数据流
Apr 13 Golang
GO语言字符串处理函数之处理Strings包
Apr 14 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 获取本地IP代码
2013/06/23 PHP
php读取csv实现csv文件下载功能
2013/12/18 PHP
如何用PHP来实现一个动态Web服务器
2015/07/29 PHP
PHP函数func_num_args用法实例分析
2015/12/07 PHP
JS面向对象、prototype、call()、apply()
2009/05/14 Javascript
jquery自动完成插件(autocomplete)应用之PHP版
2009/12/15 Javascript
jQuery对象[0]是什么含义?
2010/07/31 Javascript
Jquery时间验证和转换工具小例子
2013/07/01 Javascript
JavaScript获取/更改文本框的值的实例代码
2013/08/02 Javascript
jQuery中delegate和on的用法与区别详细解析
2014/01/26 Javascript
JavaScript对象的property属性详解
2014/04/01 Javascript
js+css实现tab菜单切换效果的方法
2015/01/20 Javascript
javascirpt实现2个iframe之间传值的方法
2016/06/30 Javascript
浅谈JavaScript的函数及作用域
2016/12/30 Javascript
JavaScript禁止微信浏览器下拉回弹效果
2017/05/16 Javascript
详解Angular中通过$location获取地址栏的参数
2018/08/02 Javascript
基于vue 动态菜单 刷新空白问题的解决
2020/08/06 Javascript
js 执行上下文和作用域的相关总结
2021/02/08 Javascript
[01:02:17]2014 DOTA2华西杯精英邀请赛 5 24 DK VS VG
2014/05/26 DOTA
[14:50]2018DOTA2亚洲邀请赛开幕式
2018/04/03 DOTA
[01:20:06]TNC vs VG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
Python 的内置字符串方法小结
2016/03/15 Python
python opencv读mp4视频的实例
2018/12/07 Python
windows10下安装TensorFlow Object Detection API的步骤
2019/06/13 Python
Win10下python 2.7与python 3.7双环境安装教程图解
2019/10/12 Python
python 调试冷知识(小结)
2019/11/11 Python
Python通过VGG16模型实现图像风格转换操作详解
2020/01/16 Python
谈谈Python:为什么类中的私有属性可以在外部赋值并访问
2020/03/05 Python
购买200个世界上最好的内衣品牌:Bare Necessities
2017/02/11 全球购物
美国球鞋寄卖网站:Stadium Goods
2018/05/09 全球购物
大学本科生的个人自我评价
2013/12/09 职场文书
毕业生个人求职自荐信
2014/02/26 职场文书
关于孝道的演讲稿
2014/05/21 职场文书
写给女朋友的检讨书
2015/05/06 职场文书
学校社团活动总结
2015/05/07 职场文书
python如何读取.mtx文件
2021/04/22 Python