這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。Go 語言是內建GC的, 相對C語言等的手動記憶體管理省事很多, 弊端便是會消耗更多的記憶體, 以及在GC時導致整個程式的停頓. 在某些特殊場合, 如果能夠可選地手動進行記憶體管理, 效果會好不少.
Go 目前的 GC 實現比較簡單(mark-sweep演算法), 進程的記憶體使用量量取決於兩次GC操作直接的記憶體申請量(不能重複使用), 而且通常GC發生在函數調用的深處, 大量對象無法立即釋放. 另外, 目前Go對記憶體的使用是貪婪的, 一旦向系統申請了就不再釋放, 進一步增大了記憶體消耗(但不是泄露). 整體看來, 對某些有大量臨時記憶體的應用, 記憶體消耗量可能會是同樣功能的C程式10倍, 甚至更多.
Beans 的 Proxy 是用 Go 實現的, 其中一個部署圖片和歌曲的執行個體也面臨了這個問題, 運行一段時間後記憶體的使用量會增長到3-4G (與訪問量相關), 另一個儲存小對象的執行個體則穩定在100M以內. Proxy 的每次請求, 都要申請一個平均 100k (10k - 3M) 的buffer用來臨時儲存資料, 它佔了整個記憶體消耗的絕大部分, 如果能夠手動管理這些buffer的使用, 應該能夠大大降低記憶體消耗.
runtime 模組有 Alloc() 和 Free(), 能夠申請後釋放記憶體, 通過refect模組做類型轉換後能夠給buffer使用. 但是它申請和釋放的記憶體也是有GC統一管理的, 一旦申請就不再還給系統. 因此我們需要把系統的malloc() 和 free() 直接封裝了給Go調用, 通過 CGO 可以簡單實現, 如下:
package cmem//include <stdlib.h>import "C"import "unsafe"func Alloc(size uintptr) *byte { return (*byte)(C.malloc(_Ctypedef_size_t(size)))}func Free(ptr *byte) { C.free(unsafe.Pointer(ptr))}
在需要使用手動分配記憶體的地方:
//item.Body = make([]byte, length)item.alloc = cmem.Alloc(uintptr(length))item.Body = (*[1 << 30]byte)(unsafe.Pointer(item.alloc))[:length](*reflect.SliceHeader)(unsafe.Pointer(&item.Body)).Cap = length
一旦臨時對象使用完畢, 可以立即釋放記憶體:
if item.alloc != nil { cmem.Free(item.alloc) item.alloc = nil}
另外, 為了防止記憶體泄露(某些情況下漏了主動是否記憶體), 可以使用runtime的Finalize機制來釋放記憶體:
runtime.SetFinalizer(item, func(item *Item) { if item.alloc != nil { cmem.Free(item.alloc) item.alloc = nil }})
文章出處:http://www.douban.com/note/149163333/