关于Go语言手动管理内存的见解

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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值