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

时间:2015-12-23 13:32:56

标签: arrays string go memory-leaks slice

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

2 个答案:

答案 0 :(得分:5)

不,这个例子不会导致内存泄漏,因为每次调用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,这样你可以更好地控制分配,并且可以在需要时复制字节。

答案 1 :(得分:3)

将切片表达式应用于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返回一个副本。

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

buf += s

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