Go 語言中手動記憶體管理
來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Go 語言中手動記憶體管理
2011-05-05 14:59:31http://www.douban.com/note/149163333/
Go 語言是內建GC的, 相對C語言等的手動記憶體管理省事很多, 弊端便是會消耗更多的記憶體, 以及在GC時導致整個程式的停頓. 在某些特殊場合, 如果能夠可選地手動進行記憶體管理, 效果會好不少.
Go 目前的 GC 實現比較簡單(mark-sweep演算法), 進程的記憶體使用量量取決於兩次GC操作直接的記憶體申請量(不能重複使用), 而且通常GC發生在函數調用的深處, 大量對象無法立即釋放. 另外, 目前Go對記憶體的使用是貪婪的, 一旦向系統申請了就不再釋放, 進一步增大了記憶體消耗(但不是泄露). 整體看來, 對某些有大量臨時記憶體的應用, 記憶體消耗量可能會是同樣功能的C程式10倍, 甚至更多.
Beansdb 的 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
}
})
通過這種簡單策略, 可以大大減少這種大的臨時對象對記憶體的消耗, Proxy 在連續運行幾天后記憶體也穩定在 200-300M 左右, 即使短時間內記憶體消耗上升, 之後如果訪問壓力下降, 記憶體使用量量也會降下來.