这种类型的 golang 字符串切片会在底层字节数组中泄漏内存吗?

Can this type of golang string slicing leak memory in underlying byte array?

buf = buf[n:]这样的golang字符串切片会导致底层字节数组内存泄漏吗?

如果是这样,是否可以获取有关底层字节数组的任何信息(如容量或基本内存地址)以验证泄漏?

请看下面的示例代码:

var buf string

func push(s string) {
    buf += s
    if len(buf) > 3 {
        buf = buf[len(buf)-3:] // can this line leak memory in underlying byte array?
    }
    fmt.Printf("buf=[%v]\n", buf)
}

Run it on playground

不,此示例不会导致内存泄漏,因为每次调用 push 时都需要分配新的字符串。有时可能会保留一些字节以减少分配,但它是如何工作的是一个不应考虑的实现细节。

如果您正在考虑分配切片操作的结果但从不追加时可能出现的类似情况。只要您了解切片的语义,就不会泄露任何信息。

s := make([]byte, 1024)
s = s[1000:]
fmt.Println(s, len(s), cap(s))

此示例将分配前 1000 个字节,但无法访问。答案很简单,不要那样做。这并不难避免,如果您确实需要确保已释放底层数组,请使用 copy 将字节移动到新的切片。

这对字符串同样适用:

s = s[1020:]
// may leave the first 1000 bytes allocated

这又是相当容易看到发生了什么,并避免。如果您使用的是大字符串,通常最好还是使用 []byte ,这样您可以更好地控制分配,并且可以在需要时复制字节。

p := s[i:j]中的字符串应用切片表达式的结果是一个字符串。据我所知,Go 语言规范 (https://golang.org/ref/spec) 没有指定 p 将由与 s.

相同的内存支持

然而,在 Go 1.6 和更早的版本中,对 p 的实时引用将阻止 s 被垃圾收集。然而,这可能会在 Go 的未来版本中改变。

一个有趣的事实是 Java String.substring 方法在 Java 8 之前以相同的方式实现。然而在 Java 8 substring returns一份。

回到你的例子。每次调用 push 函数时,以下行实际上都会创建一个新的字符串实例:

buf += s

旧的 buf 实例被垃圾收集。所以你的例子没有受到上述问题的影响。