ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Go Slice与String内存布局和实现

2022-01-31 17:59:30  阅读:157  来源: 互联网

标签:slice String 指针 Slice 数组 Go size append 底层


  上一篇提到的关于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倍。

  

标签:slice,String,指针,Slice,数组,Go,size,append,底层
来源: https://blog.csdn.net/woliuqiangdong/article/details/122761019

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有