这种类型的Golang字符串切片会导致底层字节数组泄漏内存吗?

huangapple go评论83阅读模式
英文:

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

问题

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

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

请参考下面的示例代码:

var buf string

func push(s string) {
    buf += s
    if len(buf) > 3 {
        buf = buf[len(buf)-3:] // 这行代码会导致底层字节数组的内存泄漏吗?
    }
    fmt.Printf("buf=[%v]\n", buf)
}

在playground上运行

英文:

Can golang string slicing like buf = buf[n:] cause memory leak in the underlying byte array?

If so, is it possible to get any information (like capacity, or base memory address) about the underlying byte array, in order to verify the leak?

Please see sample code below:

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][1]

[1]: http://play.golang.org/p/ad2_j2eGQh "Playground"

答案1

得分: 5

不,这个例子不会导致内存泄漏,因为每次调用push时都需要分配新的字符串。可能会有一些字节在某些时候保留下来以减少分配,但这是一个实现细节,不应该考虑进去。

如果你在考虑类似的情况,即在分配结果后没有进行追加的情况下可能出现的情况。严格来说,并没有泄漏,只要你理解切片的语义。

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

这个例子会保留前1000个字节的分配空间,但无法访问。解决这个问题很简单,不要这样做。避免这种情况并不难,如果确实需要释放底层数组,可以使用copy将字节移动到一个新的切片中。

对于字符串也是一样的:

s = s[1020:]
// 可能会保留前1000个字节的分配空间

这个问题也很容易理解和避免。如果你使用的是大型字符串,最好使用[]byte,这样你可以更好地控制分配,并在需要时复制字节。

英文:

No, this example can't cause a memory leak, since you need to allocate new strings each time you call push. It's possible that some bytes will be kept around at times to reduce allocations, but how that works is an implementation detail that shouldn't be taken into consideration.

If you're thinking of a similar situation which can arise when you assign a result of a slice operation, but never append. There's no leak per say, as long as you understand the semantics of slicing.

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

This example will leave the first 1000 bytes allocated, but inaccessible. The answer to this is simple, don't do that. It's not hard to avoid, and if you do need to ensure that you've released the underlying array, use copy to move the bytes to a new slice.

This works the same with strings:

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

This is again fairly easy to see what's going on, and avoid. If you're using huge strings, you're often better off using []byte anyway, where you have better control over allocation, and can copy the bytes when needed.

答案2

得分: 3

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

然而,在Go 1.6及更早版本中,对p的活动引用会阻止s被垃圾回收。然而,这在将来的Go版本中可能会发生变化。

有趣的是,在Java中,直到Java 8,String.substring方法也是以相同的方式实现的。然而,在Java 8中,substring返回一个副本。

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

buf += s

旧的buf实例会被垃圾回收。因此,你的例子不会受到上述问题的影响。

英文:

A result of applying slice expression to a string in p := s[i:j] is a string. As far as I can tell the Go language specification (https://golang.org/ref/spec) doesn't specify that p will be backed by the same memory as s.

However it looks like in Go 1.6 and earlier versions a live reference to p will hold s from being garbage collected. This might however change in the future versions of Go.

An interesting fact that in Java String.substring method was implemented in the same way until Java 8. In Java 8 however substring returns a copy.

Back to your example. The following line actually creates a new string instance every time you call push function:

buf += s

The old instance of buf gets garbage collected. So your example is not affected by the problem described above.

huangapple
  • 本文由 发表于 2015年12月23日 21:32:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/34436863.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定