Go清空Slice的两种方法
前言
最近在项目中用到了sync.Pool来复用我的结构体, 但遇到了点问题。
我的结构体中有几个切片, 并且这几个切片的最终size是比较大的, 但是我的项目中有不稳定因素, 因此这里无法使用固定长度的数组来代替。
可是我使用sync.Pool的目的就是为了复用这些 size, 从而降低GC的压力, 但 对于sync.Pool的使用中, 我必须在 Put 前, 重置某些可能影响复用的字段, 当然, 这些字段中就包含我的这两个切片, 我尝试用 nil切片(nil slice)
->即赋值为’nil’ 或是 空切片(empty slice)
->即赋值为 ‘[]string{}’ 或 ‘make{[]string,0,0}’ 来对其进行重置, 可由于这种重置过程, 改变了切片底层指针字段指向的地址, 进而造成 原有底层数组 失去引用, 被GC标记给干掉。 于是乎, 我的 我的sync.Pool 实际上只复用了我的结构体对象所占用的size, 并没有复用到 对象中 字段所 指向的 原有堆空间地址(如切片的底层数组), 最终不光没有降低GC的压力, 反而伴随sync.Pool的引入还增加了开销(增加开销的原因是: 其底层会使得GPM机制被短暂的强制绑定, 从而降低了性能)。
方法
经搜索, 找到了解决方法 -> 那就是 使用 [:0]
来清空切片
这种方法得到的空切片, 可以保留切片底层原本的指针字段所指向的地址, 也就是说底层数组并没有被放弃引用, 从而这部分空间就不会被GC给清楚掉。
以下内容来自文章-> (https://islishude.github.io/blog/2020/09/22/golang/
如果要清空一个slice,那么可以简单的赋值为nil,垃圾回收器会自动回收原有的数据。
1 | a := [1,2,3] |
nil slice 和普通 slice一样可以使用 cap len 内置函数,以及被 for range 遍历。本质和 empty slice 性质一样,零长度和零容量,当然也可以使用 append 操作。
但是如果还需要使用 slice 底层内存,那么最佳的方式是 re-slice:
1 | a := [1,2,3] |
不过如果序列化成 json 时候,上述二者就不太相同了,nil slice 会编码成 null
,而 empty slice 会编码成 []
。