切片截取(s := big[:100])只复制切片头(指针、len、cap),底层数组仍被整个引用。只要小切片活着,大底层数组就无法被 GC 回收——哪怕只用其中几个元素。典型事故:从文件读入大切片后只保留一小段、LRU 缓存存的是大切片的子切片、字符串子串同理(子串引用整个原字符串)。
解决方案:显式拷贝,切断对原数组的引用。
// 危险:sub 拖着整个 1GB 底层数组
sub := big[:10]
// 正确:拷贝到独立数组
sub := make([]int, 10)
copy(sub, big[:10])
// 字符串子串可用 strings.Clone / string([]byte(...))
s2 := strings.Clone(s[:10])
另一种相关场景:切片缩容(s = s[:0])后元素仍占内存,对存指针的切片应先把尾部置 nil 再截断,否则 GC 看不到元素已释放。
排查手段:pprof heap 的 inuse_space 与 alloc_space 对比,若存活远小于分配且 RSS 不降,怀疑这类引用滞留。追问方向:GC 对指针和非指针元素的扫描差异、strings 与 bytes 的 Clone 语义、内存归还操作系统(scavenger)的行为。