Go Slice与String内存布局和实现
2022/2/1 7:29:15
本文主要是介绍Go Slice与String内存布局和实现,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
上一篇提到的关于gc性能的问题,对比slice和map的结构可以看出为了存储数据map用了更多的内存空间,并且可能存在链表,链表的每个节点在gc时都做为一个小对象对待,增加了扫描的时间,因此gc时间相对更长。
slice通过内部指针和相关属性引用数组片段,来实现变长方案。实现方式和数据结构都类似C++中的vector。它本身是结构体,作为参数传递时传递的是slice本身而不是它引用的底层数组。len()可获得slice长度,cap()可获得slice容量。
slice可以通过数组初始化,也可以直接make。make时直接使用cap作为new的长度来创建底层数组,返回的是slice结构体。如果通过new([]int)来初始化,它返回的是一个指向slice结构体的指针,不能直接对它进行下标操作。
遍历slice时经常用到range操作,range会复制range的对象。下面例子中在循环内部改变slice的属性,最终会作用到slice上导致最后输出[1 2 101]。但是并不会导致循环在第三次就结束,因为range s是从s的复本中读取i和n的。s的复本只复制了指针,底层元素仍指向同一片,因此可以在循环内改变slice元素的值并在循环期内可见。
reslice的增长规则:如果新的size是当前size的2倍以上,则大小增长为新size。如果新的size不到当前size的2倍,则按当前size的不同有不同操作。当前size不超过1024,按每次2倍增长,否则按当前大小的1/4增长。
slice通过append元素使得元素达到cap,就会重新分配内存,复制内容并接着append,即便指向的数组还有空位。比如这个例子a初始化为长度和容量都是3的slice,再往a中append数据时a将在堆上重新分配空间并复制原始内容,因此这时原始数组的后几位已经看不到了。
如果slice作为函数的入参,通常希望对slice的操作可以影响到底层数据,但是如果在函数内部append数据超过了cap,导致重新分配底层数组,这时入参a指向的底层数组跟调用方实参指向的不再是同一个。如下面的例子这样因为扩容导致与代码实现原意相违背,因此通常不建议在函数内部对slice有append操作,若有需要则显示的返回这个slice。
string的结构和C++STL实现的string类似。都是由指向固定地址的str指针和len组成的结构体。对string的复制只是对指针和len的复制,作为函数参数时入参只不过是指向同一个底层数据的相同指针。
通常string常量是编译器分配到只读段的(.rodata),对应的数据地址不可写入。fmt.Sprintf生成的字符串分配在堆上,对应数据地址可修改。
平常使用中经常将两者互相转化,每次相互转化时都会发生底层数据的复制。如果是动态生成的字符串可以通过以下对指针的操作来直接转化数据,而不需要拷贝,性能好接近4倍。
这篇关于Go Slice与String内存布局和实现的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-24MongoDB资料:新手入门完全指南
- 2024-12-20go-zero 框架的 RPC 服务 启动start和停止 底层是怎么实现的?-icode9专业技术文章分享
- 2024-12-19Go-Zero 框架的 RPC 服务启动和停止的基本机制和过程是怎么实现的?-icode9专业技术文章分享
- 2024-12-18怎么在golang中使用gRPC测试mock数据?-icode9专业技术文章分享
- 2024-12-15掌握PageRank算法核心!你离Google优化高手只差一步!
- 2024-12-15GORM 中的标签 gorm:"index"是什么?-icode9专业技术文章分享
- 2024-12-11怎么在 Go 语言中获取 Open vSwitch (OVS) 的桥接信息(Bridge)?-icode9专业技术文章分享
- 2024-12-11怎么用Go 语言的库来与 Open vSwitch 进行交互?-icode9专业技术文章分享
- 2024-12-11怎么在 go-zero 项目中发送阿里云短信?-icode9专业技术文章分享
- 2024-12-11怎么使用阿里云 Go SDK (alibaba-cloud-sdk-go) 发送短信?-icode9专业技术文章分享