无法释放由bytes.Buffer占用的内存

11

我收到压缩的ASCII文本字节,存放在类型为[]byte的compressedbytes中。我面临的问题是,在函数结束后,下面的过程会占用大量内存,并且在程序运行时保持占用状态。

    b := bytes.NewReader(compressedbytes)
    r, err := zlib.NewReader(b)
    if err != nil {
        panic(err)
    }
    cleartext, err = ioutil.ReadAll(r)
    if err != nil {
        panic(err)
    }

我注意到正在使用的类型是bytes.Buffer,这种类型具有Reset()Truncate()函数,但它们都不允许释放一次占用的内存。

Reset()的文档如下:

Reset将缓冲区重置为空,但保留底层存储以供将来写入使用。Reset与Truncate(0)相同。

我该如何取消设置缓冲区并再次释放内存? 我的程序在运行时需要大约50MB的内存,运行时间为2小时。当我导入zlib压缩的字符串时,程序需要200 MB的内存。

感谢您的帮助。

=== 更新

我甚至为解压缩创建了一个单独的函数,并在程序从该函数返回后手动调用垃圾收集器runtime.GC(),但没有成功。

// unpack decompresses zlib compressed bytes
func unpack(packedData []byte) []byte {
    b := bytes.NewReader(packedData)
    r, err := zlib.NewReader(b)
    if err != nil {
        panic(err)
    }
    cleartext, err := ioutil.ReadAll(r)
    if err != nil {
        panic(err)
    }
    r.Close()
    return cleartext
}

使用 b.WriteTo(os.stdout) 排空缓冲区怎么样? - khrm
1
你是如何测量内存使用情况的? - elithrar
我在这个语句之前和之后放置了一个暂停(等待用户输入),并观察过程内存的增长。我在SysInternals ProcessExplorer中检查它,列为“WorkingSetSize”和“PrivateBytes”。 - JohnGalt
谢谢您的建议,khrm。我试图添加 b.WriteTo(ioutil.Discard) ,但没有成功。 - JohnGalt
2
@JohnGalt 这不是检查内存是否被GC收集的正确方法。别担心。GC会收集未使用的内存,可能会或可能不会将其返还给操作系统,这很好。 - Volker
2个回答

45
一些需要澄清的事情。Go是一种带有垃圾回收机制的编程语言,这意味着当变量不可达时(如果您有另一个指向该变量的指针,则仍视为“可达”),变量使用的内存会自动被垃圾回收器释放。
释放的内存并不意味着它会归还给操作系统。释放的内存意味着该内存可以被回收,如果需要,可以重用于另一个变量。因此,当某个变量不可达且垃圾回收器检测到此情况并释放了其使用的内存时,操作系统中不会立即看到内存减少。
然而,如果一段时间内未使用该内存(通常约为5分钟),Go运行时将将该内存返回给操作系统。如果在此期间内存使用量增加(并可选地再次缩小),则该内存很可能不会返回给操作系统。
如果您等待一段时间并且不再分配内存,则释放的内存最终将归还给操作系统(显然不会全部归还,但未使用的“大块”将被归还)。如果您无法等待此过程发生,您可以调用debug.FreeOSMemory()来强制执行此行为:

FreeOSMemory 强制进行垃圾回收,然后尝试将尽可能多的内存返回给操作系统。(即使没有调用此函数,运行时也会逐渐在后台任务中将内存返回给操作系统。)

请查看这种旧但真正有用的问题和答案:

Go 1.3 垃圾收集器无法将服务器内存释放回系统


1
非常感谢 - 那帮了大忙。 - JohnGalt

2

当没有任何引用时,它最终会被释放。Go语言的垃圾回收器相当不错。


我甚至将所有内容放在一个单独的函数中,在函数返回后调用runtime.GC()垃圾收集器,但它仍然没有被垃圾收集器清理掉。 - JohnGalt
3
@JohnGalt:垃圾收集器的工作不是释放内存。堆清道夫可能在运行时向操作系统提供可用内存,但这与GC无关。 - JimB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接