Go 語言中手動記憶體管理

2021-09-30 10:38:57 字數 1406 閱讀 9319

2011-05-05 14:59:31

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

import "c"

import "unsafe"

func alloc(size uintptr) *byte

func free(ptr *byte)

在需要使用手動分配記憶體的地方:

= 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

另外, 為了防止記憶體洩露(某些情況下漏了主動是否記憶體), 可以使用runtime的finalize機制來釋放記憶體:

runtime.setfinalizer(item, func(item *item)

})通過這種簡單策略, 可以大大減少這種大的臨時物件對記憶體的消耗, proxy 在連續執行幾天後記憶體也穩定在 200-300m 左右, 即使短時間內記憶體消耗上公升, 之後如果訪問壓力下降, 記憶體使用量也會降下來.

C語言中的記憶體管理

先說一下c語言中的記憶體管理。1.動態記憶體分配 原因 程式執行過程中,很有可能需要一些額外的記憶體空間。動態記憶體從 來,還給誰?這塊是記憶體是系統專門預留出來的,給程式動態的分配和動態的歸還的。當free函式的引數為空的時候,那麼我們的free什麼事都不做了。clloc和realloc的用法見截...

C語言中記憶體管理規範

一 記憶體申請 1.建議使用calloc申請記憶體,盡量不要使用malloc。calloc在動態分配完記憶體後,自動初始化該記憶體空間為零,而malloc不初始化,裡邊資料是隨機的垃圾資料。2.申請記憶體大小必須大於0.1 使用0位元組長度申請記憶體的行為是沒有定義的,在引用記憶體申請函式返回位址時...

OC 手動記憶體管理

一.為什麼要進行記憶體管理 建立乙個oc物件 定義乙個變數 呼叫乙個函式或者方法 二.oc中的記憶體管理是管理的記憶體中的哪一部分 我們知道 記憶體分為五大區域 棧區,堆區,資料去,bss段,區。而最後三者是在程式啟動時由作業系統進行控制的,所以不需要我們管理。棧區儲存的也都是已經初始化的基本資料型...