1. 程式人生 > >Go 語言中手動記憶體管理

Go 語言中手動記憶體管理

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 左右, 即使短時間內記憶體消耗上升, 之後如果訪問壓力下降, 記憶體使用量也會降下來.

相關推薦

Go 語言手動記憶體管理

Go 語言是自帶GC的, 相對C語言等的手動記憶體管理省事很多, 弊端便是會消耗更多的記憶體, 以及在GC時導致整個程式的停頓. 在某些特殊場合, 如果能夠可選地手動進行記憶體管理, 效果會好不少. Go 目前的 GC 實現比較簡單(mark-sweep演算法), 程序的記憶體使用量取決於兩次GC操作直接的

Go語言的工程管理

1、安裝Go並新增編譯環境 1、下載安裝包,[Go網站](https://golang.org/) 2、安裝好之後,把安卓目錄中的bin目錄加入到path中去,我想有過java學習經歷的朋友對這個肯定

Go -- 在Go語言使用JSON struct

字母 ims bgcolor board channel rgs pick marshal player Encode 將一個對象編碼成JSON數據,接受一個interface{}對象,返回[]byte和error: func Marshal(v interface{}

如何在GO語言使用Kubernetes API?

GO KUbernetes Kubernetes API 微服務 控制器 Rancher Labs首席軟件工程師Alena Prokharchyk受邀在2017年12月6-8日的CNCF主辦的Kubernetes領域頂級盛會KubeCon + CloudNativeCon 2017北美峰會

002_解析go語言的回調函數

println 函數類 () pos span 示例 lB 解析 作用 回調函數是一種特殊的函數寫法,在很多場景中發揮廣泛的作用。但是對於初學者來說,回調函數是比較頭疼的一個東西,不太好懂,筆者研究了一番,以網上的一個例子詳細說明一下 首先看一個代碼示例(來源於網上) pa

go語言通過http訪問需要認證的api

read light {} true 訪問 users tps ada 返回    func main() { //生成client 參數為默認 client := &http.Client{} //生成要訪問的url url := "https://a

005_針對於go語言速率限制的思考

回來 條件 chan 針對 完成 結果 int ++ if條件 在之前的go語言的速率限制這篇文章裏,我們嘗試了普通的速率限制,和脈沖型速率限制。其中,脈沖型速率限制是放開了限制,裏面有3個請求是一次性到達,然後再按照200ms的速度限制的,之前的代碼如下所示: pack

Go語言正則表達式的使用

Go語言正則表達式 Go語言正則表達式的使用 Go語言 正則表達式 許多程序語言都支持使用正則表達式對字符串進行操作,Go語言也不例外,正則表達式的語法網上很多教程,本文主要介紹在Go語言中如何使用正則表達式,通過以下實例進行說明,並添加了詳細的註釋,不會的小夥伴一看就明白了。 func ma

Go 語言數據類型的判斷

Go 語言中數據類型的判斷 Go數據類型判斷 Go數據類型 Go 語言中數據類型的判斷,本文介紹三種方法。方法一:使用i.(type)結合空接口(interface{}) func main() { v1 := "中國你好" v2 := 20 var v3 byte =

Go語言的反射機制reflect

Go語言中的反射機制reflect Go語言反射機制 Go語言reflect go的反射機制是要通過接口來進行的,而類似於Java的Object的空接口可以和任何類型進行交互,因此對基本數據類型等的反射也直接利用了這一特點。 package main import ( "fmt"

Go語言日誌處理,log包的使用

Go語言日誌 Go語言log golang日誌處理 Golang提供了原生日誌庫“log”,使用簡單方便,本文以代碼為例進行說明介紹。 package main import ( "os" "log" "fmt" ) func main() { // 打開日誌文

各種加密算法在Go語言的使用

各種加密算法在Go語言中的使用 Go語言使用加密算法 Go語言加密 使用SHA256、MD5、RIPEMD160 import ( "fmt" "crypto/sha256" "os" "io" "crypto/md5" "golang.org/x/

Go語言時間函數及定時器的使用

Go語言時間函數 Go語言定時器 Go語言中時間函數及定時器、休眠等功能的實現和使用,代碼如下,有需要的小夥伴直接拿去 package main import ( "time" "fmt" ) func main() { // 設置時區,如果name是""或"UTC",返回

Go語言的單例模式

syn main 單例模式 return and type KS int pack 單例模式 使用單例的目的是為了保證在整個系統中存在唯一的實例 package main import ( "fmt" "sync" ) type IntA

Go語言的控制語句

sel pre pytho Go語言 跳轉 sele index project sleep // code_004_process_control project main.go package main import ( "fmt" "time" )

Go語言的常量

識別 如果 集中管理 圓周率 導出 數據 變量 hat 一次 1 概述 常量,一經定義不可更改的量。功能角度看,當出現不需要被更改的數據時,應該使用常量進行存儲,例如圓周率。從語法的角度看,使用常量可以保證數據,在整個運行期間內,不會被更改。例如當前處理器的架構類型,可以保

Go語言的運算符

成語 就是 號碼 嚴格 內存地址 type 更改 能夠 存儲空間 1 概述 Go語言提供了,算術,關系,邏輯,位,指針,賦值運算符。本篇整體說明一下。 2 算術運算 + 相加 - 相減 * 相乘 / 相除 % 求余 ++ 自增 -- 自減 幾個細節,需要留意一下。 整

Go語言多字節字符的處理

語言環境 對應關系 相對 全部 special 每一個 圖片 轉換 完整 1 概述 Go語言的字符串是使用 UTF-8 編碼的。UTF-8 是 Unicode 的實現方式之一。本文內容包括:UTF-8 和 Unicode 的關系,Go語言提供的 unicode 包和 uni

Go語言的字符串處理

定義 sizeof gui bst utf ges 替換字符串 asp equal 1 概述 字符串,string,一串固定長度的字符連接起來的字符集合。Go語言的字符串是使用UTF-8編碼的。UTF-8是Unicode的實現方式之一。 Go語言原生支持字符串。使用雙引號(

Go語言的變量

都是 可見 字符 代碼 不可見 快速 字符串變量 例如 重新 1 概述 變量(Variable)是程序運行過程中,內容可以變化(修改)的量,變量的功能是存儲用戶的數據,是計算機語言中能儲存計算結果或能表示值抽象概念。變量,是通過變量的標識符定位值的過程。變量的內存模擬演示: