這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。這是一篇簡短的筆記,關於你或許會遇上的 Go 語言的記憶體不足的問題。如你所知,Go 語言的 slice 很強大且使用簡單。通過 Go 語言的內建函數,它可以解決我們許多問題。但是今天,我更多想講述的是 slice 與它的記憶體。首先,建立 slice 最簡單的方式是:```govar sliceOfInt []intvar sliceOfString []intvar sliceOfUser []Uservar sliceOfMap []map[string]intletters := []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 函數放到迴圈或嵌套迴圈中執行,快速的迴圈沒什麼問題,但是更大的迴圈,可能會引發問題,它會急速消耗記憶體。```govar largeOfList []intvar result []intfor _, item := range largeOfList { if item.Status == true { result = append(result, item) }}```你可以想象,每一個迴圈都會分配一個擁有新的容量的新記憶體,並且它還沒來得及釋放就得記憶體,我們就會看到以下錯誤```fatal error: runtime: out of memoryruntime stack:runtime.throw(0xd4b870, 0x16)/home/sss/go/src/runtime/panic.go:530 +0x90runtime.sysMap(0xc88b750000, 0x2f120000, 0xc8204ec000, 0x1113238)/home/sss/go/src/runtime/mem_linux.go:206 +0x9bruntime.(*mheap).sysAlloc(0x10f8c60, 0x2f120000, 0x2000000000002)/home/sss/go/src/runtime/malloc.go:429 +0x191runtime.(*mheap).grow(0x10f8c60, 0x17890, 0x0)/home/sss/go/src/runtime/mheap.go:651 +0x63runtime.(*mheap).allocSpanLocked(0x10f8c60, 0x1788e, 0x7f4b42f275f0)/home/sss/go/src/runtime/mheap.go:553 +0x4f6runtime.(*mheap).alloc_m(0x10f8c60, 0x1788e, 0x100000000, 0x7f4b417f0dd0)/home/sss/go/src/runtime/mheap.go:437 +0x119runtime.(*mheap).alloc.func1()/home/sss/go/src/runtime/mheap.go:502 +0x41runtime.systemstack(0x7f4b417f0de8)/home/sss/go/src/runtime/asm_amd64.s:307 +0xabruntime.(*mheap).alloc(0x10f8c60, 0x1788e, 0x100000000, 0xc820187500)/home/sss/go/src/runtime/mheap.go:503 +0x63runtime.largeAlloc(0x2f11c000, 0x100000003, 0x434cc2)/home/sss/go/src/runtime/malloc.go:766 +0xb3runtime.mallocgc.func3()/home/sss/go/src/runtime/malloc.go:664 +0x33runtime.systemstack(0xc820014000)/home/sss/go/src/runtime/asm_amd64.s:291 +0x79runtime.mstart()/home/sss/go/src/runtime/proc.go:1048goroutine 22 [running]:runtime.systemstack_switch()/home/sss/go/src/runtime/asm_amd64.s:245 fp=0xc820200bc8 sp=0xc820200bc0runtime.mallocgc(0x2f11c000, 0x0, 0x3, 0xc8204f4000)/home/sss/go/src/runtime/malloc.go:665 +0x9eb fp=0xc820200ca0 sp=0xc820200bc8runtime.rawmem(0x2f11c000, 0x7880000)/home/sss/go/src/runtime/malloc.go:809 +0x32 fp=0xc820200cc8 sp=0xc820200ca0runtime.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語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
706 次點擊