补充反馈
slice
既然聊到slice,就不得不提它的近亲array,这里不太想提值类型和引用类型的概念(个人觉得其实都是值类型),golang的array其实可以假想为C的struct类型,只是struct通过变量名来访问成员(如xxx.yyy),而array通过下标来访问成员(如xxx[3]),具体内存布局如下图所示:
图 1 golang的array内存布局
显然golang的array灵活性比较差,长度固定,这才有了slice,概念上有点类似于STL的vector,但是具体实现上还是有差距的,具体内存布局如下图所示:
图 2 golang的slice内存布局
slice类型存在len和cap的概念(与vector类似),这里有一点需要澄清:与vector不一样,slice的len并不能无限增长,cap就是它的天花板。比如s := make([]byte, 3, 5),后续不管s如何增长,它的len也不能超过5。
s:= make([]byte, 5)
s= s[2:4]
图 3 s[2:4]内存布局
从上图不难看出,其实slice操作并没有任何内存拷贝动作,仅仅是生成一份新的描述数据(len=2 cap=3);此时,如果执行s = s[:cap(s)],可以将s的len扩张到最大,如下图所示:
图 4 s[:cap(s)]内存布局
在golang里面,如果slice需要扩张到超出cap,只能创建新的slice,然后将现有数据copy过去,再指向新的slice,一般可以借助内置的append函数。
顺带一提,由于slice操作之后,新的对象存在指针指向真实的数据块内存,所以某些场景下,可能会导致大块内存无法被GC回收。
performance
不少tx都提到了深深的性能担忧,其实我本来并不喜欢过于纠结性能问题,毕竟追求极致的单机性能往往意义不大,不过既然提到了,我也上网找了点数据,供有兴趣的读者参考: