如何释放片分配的内存?

亚历山大·马可夫(Aleksandr Makov):
package main

import (
    "fmt"
    "time"
)

func main() {
    storage := []string{}

    for i := 0; i < 50000000; i++ {
        storage = append(storage, "string string string string string string string string string string string string")
    }

    fmt.Println("done allocating, emptying")

    storage = storage[:0]
    storage = nil

    for {
        time.Sleep(1 * time.Second)
    }
}

上面的代码将分配大约30mb的内存,然后将不会释放它。这是为什么?如何强制去释放此片使用的内存?我将那片切成薄片,然后将其切碎。

我正在调试的程序是一个简单的HTTP输入缓冲区:它将所有请求附加到大块中,然后通过通道将这些块发送到goroutine进行处理。但是问题在上面已说明-我无法获得存储来释放内存,然后最终耗尽内存。

编辑:一些人指出,类似的问题,不,先不工作,第二个是不是我要求的。切片会清空,而内存不会清空。

成本:

这里发生了几件事。

首先需要理解的是Go是一种垃圾收集语言。它的GC的实际算法基本上无关紧要,但是要理解它的一个方面至关重要:它不使用引用计数,因此无法以某种方式使GC立即回收任何给定值的内存,该给定值的存储分配在堆。用更简单的话概括一下,这样做是徒劳的

s := make([]string, 10*100*100)
s = nil

因为第二条语句确实将删除对切片的基础内存的唯一引用,但不会使GC继续运行并将该内存“标记”为可重用。

这意味着两件事:

  • 您应该知道GC的工作方式。说明了从v1.5到现在(目前是v1.10)的工作方式。
  • 您应该以减少内存压力的方式来构造那些占用大量内存的算法。

后者可以通过几种方式完成:

  • 当您对要多少钱有一个明智的想法时,请进行预分配。

    在您的示例中,您从长度为0的切片开始,然后对其进行大量附加。现在,几乎所有处理增长的内存缓冲区(包括Go运行时)的库代码都通过以下方式处理这些分配:1)分配两倍的请求内存–希望防止将来再分配一些内存;以及2)在以下情况下复制 “旧”内容:它不得不重新分配。这一点很重要:当发生重新分配时,这意味着现在有两个内存区域:旧的和新的。

    如果您可以估计N平均需要保留元素,请使用make([]T, 0, N)- 在此处此处的更多信息为它们预先分配如果您需要容纳少于个N元素,那么该缓冲区的尾部将不被使用,并且如果您需要容纳大于个元素,则N需要重新分配,但是平均而言,您不需要任何重新分配。

  • 重新使用您的切片。假设您的情况是,可以通过将切片切片为零长度来“重置”切片,然后将其再次用于下一个请求。这称为“池”,在大规模并行访问此类池的情况下,您可以sync.Pool用来保存缓冲区。

  • 限制系统上的负载,以使GC能够应付持续的负载。两个很好的概述接近这样的限制是这个

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章