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 51单片机使lcd显示器开机闪烁预设字符闪烁3次需要加什么代码
  • ¥50 C# 使用DEVMOD设置打印机首选项
  • ¥15 麒麟V10 arm安装gdal
  • ¥15 想用@vueuse 把项目动态改成深色主题,localStorge里面的vueuse-color-scheme一开始就给我改成了dark,不知道什么原因(相关搜索:背景颜色)
  • ¥20 OPENVPN连接问题
  • ¥15 flask实现搜索框访问数据库
  • ¥15 mrk3399刷完安卓11后投屏调试只能显示一个设备
  • ¥100 如何用js写一个游戏云存档
  • ¥15 ansys fluent计算闪退
  • ¥15 有关wireshark抓包的问题