Go 语言中 append 导致内存不足

lightfish-zhang · · 2942 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

这是一篇简短的笔记,关于你或许会遇上的 Go 语言的内存不足的问题。 如你所知,Go 语言的 slice 很强大且使用简单。通过 Go 语言的内置函数,它可以解决我们许多问题。 但是今天,我更多想讲述的是 slice 与它的内存。 首先,创建 slice 最简单的方式是: ```go var sliceOfInt []int var sliceOfString []int var sliceOfUser []User var sliceOfMap []map[string]int letters := []string{"a", "b", "c", "d"} numbers := []string{1, 2, 3, 4, 5} numbers := []User{ User{ Name: "Thuc"}, User{Name: "Mr Vu"} } ``` 当你创建一个 slice, 将会定义三个重要的信息,包含这个数组的指针 pointer,堆栈的已使用长度 length ,以及容量 capacity (堆栈的最大长度) ![](https://raw.githubusercontent.com/studygolang/gctt-images/master/out-of-memory-with-append-in-golang/slice_memory.png) > 长度是切片引用的元素的数量 > 容量是底层数组中元素的数量(从切片指针引用的元素开始) 于是,当你使用我给出的以上方式创建 slices ,长度和容量将是最小的。 我们转下一个话题,Go 语言的 [append](https://golang.org/pkg/builtin/#append) 函数。 > 这个内置函数 append 把元素追加到 slice 的末尾,如果有足够的容量,就直接在末尾的内存加入这个新元素。如果没有足够的空间,则新分配一个新的基础的数组,追加更新的 slice 。 问题在于我们经常以为 append 会追加一个新的元素到 slice 的末尾,而忘记了如果没有足够的预留容量,这个操作会导致在新的内存上重新分配一个新的 slice 。 使用这个最简单的方式来创建 slice ,容量(capacity)总是与切片中的当前元素相匹配。不幸的是,有些源代码里是将 append 函数放到循环或嵌套循环中执行,快速的循环没什么问题,但是更大的循环,可能会引发问题,它会急速消耗内存。 ```go var largeOfList []int var result []int for _, item := range largeOfList { if item.Status == true { result = append(result, item) } } ``` 你可以想象,每一个循环都会分配一个拥有新的容量的新内存,并且它还没来得及释放就得内存,我们就会看到以下错误 ``` fatal error: runtime: out of memory runtime stack: runtime.throw(0xd4b870, 0x16) /home/sss/go/src/runtime/panic.go:530 +0x90 runtime.sysMap(0xc88b750000, 0x2f120000, 0xc8204ec000, 0x1113238) /home/sss/go/src/runtime/mem_linux.go:206 +0x9b runtime.(*mheap).sysAlloc(0x10f8c60, 0x2f120000, 0x2000000000002) /home/sss/go/src/runtime/malloc.go:429 +0x191 runtime.(*mheap).grow(0x10f8c60, 0x17890, 0x0) /home/sss/go/src/runtime/mheap.go:651 +0x63 runtime.(*mheap).allocSpanLocked(0x10f8c60, 0x1788e, 0x7f4b42f275f0) /home/sss/go/src/runtime/mheap.go:553 +0x4f6 runtime.(*mheap).alloc_m(0x10f8c60, 0x1788e, 0x100000000, 0x7f4b417f0dd0) /home/sss/go/src/runtime/mheap.go:437 +0x119 runtime.(*mheap).alloc.func1() /home/sss/go/src/runtime/mheap.go:502 +0x41 runtime.systemstack(0x7f4b417f0de8) /home/sss/go/src/runtime/asm_amd64.s:307 +0xab runtime.(*mheap).alloc(0x10f8c60, 0x1788e, 0x100000000, 0xc820187500) /home/sss/go/src/runtime/mheap.go:503 +0x63 runtime.largeAlloc(0x2f11c000, 0x100000003, 0x434cc2) /home/sss/go/src/runtime/malloc.go:766 +0xb3 runtime.mallocgc.func3() /home/sss/go/src/runtime/malloc.go:664 +0x33 runtime.systemstack(0xc820014000) /home/sss/go/src/runtime/asm_amd64.s:291 +0x79 runtime.mstart() /home/sss/go/src/runtime/proc.go:1048 goroutine 22 [running]: runtime.systemstack_switch() /home/sss/go/src/runtime/asm_amd64.s:245 fp=0xc820200bc8 sp=0xc820200bc0 runtime.mallocgc(0x2f11c000, 0x0, 0x3, 0xc8204f4000) /home/sss/go/src/runtime/malloc.go:665 +0x9eb fp=0xc820200ca0 sp=0xc820200bc8 runtime.rawmem(0x2f11c000, 0x7880000) /home/sss/go/src/runtime/malloc.go:809 +0x32 fp=0xc820200cc8 sp=0xc820200ca0 runtime.growslice(0xa4c8a0, 0xc8204f4000, 0x4b4f800, 0x4b4f800, 0x4b4f801, 0x0, 0x0, 0x0) /home/sss/go/src/runtime/slice.go:95 +0x233 fp=0xc820200d38 sp=0xc820200cc8 ``` ## 内存不足 Go 内置的代码在给 slice 分配内存空间时会预留更多的容量(capacity),这使得 append 不会因为容量不足而分配新的内存。 总而言之,我写这一篇文章,是希望我们在编程时,不妨思考多一些关于内存如何操作的问题。 希望这文章能帮到您 :)

via: https://blog.siliconstraits.com/out-of-memory-with-append-in-golang-956e7eb2c70e

作者:Thuc Le  译者:lightfish-zhang  校对:polaris1119

本文由 GCTT 原创编译,Go语言中文网 荣誉推出


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2942 次点击  
加入收藏 微博
被以下专栏收入,发现更多相似内容
3 回复  |  直到 2018-05-11 10:33:29
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传