dongxiaolin2801 2018-02-05 08:13
浏览 61
已采纳

如何释放分片分配的内存? [重复]

This question already has an answer here:

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)
    }
}

The code above will allocate about ~30mb of memory, and then won't release it. Why is that? How can I force go to release memory used by this slice? I sliced that slice and then nilled it.

The program I'm debugging is a simple HTTP input buffer: it appends all requests into large chunks, and sends these chunks over a channel to goroutine for processing. But problem is illustrated above - I can't get storage to release the memory and then eventually run out of memory.

Edit: as some people pointed out to similar question, no, it first doesn't work, second isn't what I'm asking for. The slice gets emptied, the memory does not.

</div>
  • 写回答

2条回答 默认 最新

  • doujuyang1764 2018-02-05 08:45
    关注

    There are several things going on here.

    The first one which is needed to be absorbed is that Go is a garbage-collected language; the actual algorithm of its GC is mostly irrelevant but one aspect of it is crucial to understand: it does not use reference counting, and hence there's no way to somehow make the GC immediately reclaim the memory of any given value whose storage is allocated on the heap. To recap it in more simple words, it's futile to do

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

    as the second statement will indeed remove the sole reference to the slice's underlying memory but won't make the GC go and "mark" that memory as available for reuse.

    This means two things:

    • You should know how the GC works. This explains how it works since v1.5 and up until now (v1.10 these days).
    • You should structure those of your algorythms which are memory-intensive in a way that reduces memory pressure.

    The latter can be done in several ways:

    • Preallocate, when you have a sensible idea about how much to.

      In your example, you start with a slice of length 0, and then append to it a lot. Now, almost all library code which deals with growing memory buffers—the Go runtime included—deals with these allocations by 1) allocating twice the memory requested—hoping to prevent several future allocations, and 2) copies the "old" contents over, when it had to reallocate. This one is important: when reallocation happens, it means there's two memory regions now: the old one and the new one.

      If you can estimate that you may need to hold N elements on average, preallocate for them using make([]T, 0, N)— more info here and here. If you'll need to hold less than N elements, the tail of that buffer will be unused, and if you'll need to hold more than N, you'll need to reallocate, but on average, you won't need any reallocations.

    • Re-use your slice(s). Say, in your case, you could "reset" the slice by reslicing it to the zero length and then use it again for the next request. This is called "pooling", and in the case of mass-parallel access to such a pool, you could use sync.Pool to hold your buffers.

    • Limit the load on your system to make the GC be able to cope with the sustained load. A good overview of the two approaches to such limiting is this.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 划分vlan后不通了
  • ¥15 GDI处理通道视频时总是带有白色锯齿
  • ¥20 用雷电模拟器安装百达屋apk一直闪退
  • ¥15 算能科技20240506咨询(拒绝大模型回答)
  • ¥15 自适应 AR 模型 参数估计Matlab程序
  • ¥100 角动量包络面如何用MATLAB绘制
  • ¥15 merge函数占用内存过大
  • ¥15 使用EMD去噪处理RML2016数据集时候的原理
  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大
  • ¥15 单片机无法进入HAL_TIM_PWM_PulseFinishedCallback回调函数