【问题标题】:Cannot free memory once occupied by bytes.Buffer一旦被 bytes.Buffer 占用,就无法释放内存
【发布时间】:2016-09-19 20:07:48
【问题描述】:

我在 []byte 类型的 compressedbytes 中接收到压缩的 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() 函数,但它们都不允许释放曾经被占用的内存。

Reset()documentation 声明如下:

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

如何取消设置缓冲区并再次释放内存? 在需要 2 小时的运行期间,我的程序需要大约 50MB 的内存。当我导入 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) 排空缓冲区怎么样?
  • 你如何测量内存使用情况?
  • 我在此语句前后放置了一个暂停(等待用户输入),并观察其间的进程内存增长。我在“WorkingSetSize”和“PrivateBytes”列的 SysInternals ProcessExplorer 中检查它。
  • 感谢 khrm,好主意 - 我尝试添加 b.WriteTo(ioutil.Discard) 没有成功。
  • @JohnGalt 这不是检查内存是否被 GC 收集的正确方法。别担心了。 GC 收集未使用的内存,可能会也可能不会将其交还给操作系统,这很好。

标签: memory go buffer zlib


【解决方案1】:

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

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

但是,如果一段时间(通常是 5 分钟左右)不使用,Go 运行时会将内存返回给操作系统。如果在此期间内存使用量增加(并且可选地再次缩小),则内存很可能不会返回给操作系统。

如果您等待一段时间并且不再分配内存,释放的内存最终将返回给操作系统(显然不是全部,而是未使用的“大块”)。如果您不能等待这种情况发生,您可以致电debug.FreeOSMemory() 强制执行此行为:

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

看看这种古老但内容丰富的问题​​+答案:

Go 1.3 Garbage collector not releasing server memory back to system

【讨论】:

  • 非常感谢 - 帮助了
【解决方案2】:

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

【讨论】:

  • 我什至把所有东西都放在一个单独的函数中,通过runtime.GC()从函数返回后调用垃圾收集器,它没有被垃圾收集器清理
  • @JohnGalt:释放内存不是垃圾收集器的工作。 heap scavenger 在运行时可能会向操作系统提供空闲内存,但这与 GC 无关。
猜你喜欢
  • 1970-01-01
  • 2018-07-27
  • 2015-08-10
  • 1970-01-01
  • 2013-07-30
  • 1970-01-01
相关资源
最近更新 更多