一旦被 bytes.Buffer 占用,就无法释放内存

Cannot free memory once occupied by bytes.Buffer

我在 compressedbytes 中收到 []byte 类型的压缩 ASCII 文本字节。我面临的问题是以下过程占用了大量内存,在函数结束后不会被释放,并且在程序的整个 运行 时间内一直被占用。

    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() 函数,但其​​中的 none 允许释放内存曾被占领

Reset()documentation 声明如下:

Reset resets the buffer to be empty, but it retains the underlying storage for use by future writes. Reset is the same as Truncate(0).

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

感谢您的帮助。

===更新

我什至为解压缩创建了一个单独的函数,并在该函数的程序 returns 之后用 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
}

当没有人引用它时,它最终会被释放,Go 有一个相当不错的 GC。

有些事情要搞清楚。 Go 是一种垃圾收集语言,这意味着当这些变量变得无法访问时,垃圾收集器会自动释放由变量分配和使用的内存(如果您有另一个指向该变量的指针,那仍然算作 "reachable")。

释放的内存并不意味着 returned 到 OS。释放内存意味着可以回收内存,如果需要,可以将其重新用于另一个变量。因此,从操作系统中,您不会仅仅因为某些变量变得不可访问而立即看到内存减少,垃圾收集器检测到这一点并释放了它使用的内存。

如果有一段时间(通常是 5 分钟左右)未使用,Go 运行时将 return 内存分配给 OS。如果在此期间内存使用量增加(并可选择再次收缩),内存很可能不会 returned 到 OS.

如果您等待一段时间并且不再分配内存,释放的内存最终将 returned 到 OS(显然不是全部,但未使用的 "big chunks" 将会)。如果您等不及这种情况发生,您可以调用 debug.FreeOSMemory() 来强制执行此行为:

FreeOSMemory forces a garbage collection followed by an attempt to return as much memory to the operating system as possible. (Even if this is not called, the runtime gradually returns memory to the operating system in a background task.)

看看这种古老但真正有用的问题+答案:

Go 1.3 Garbage collector not releasing server memory back to system