golang 内存池

williamjie · · 1208 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

一般来说,内存池都是采用预分配的方式,分为固定大小的和非固定大小块,固定大小的内存效率高,非固定大小灵活。同时,分为单线程和多线程版的,单线程不需要考虑并发问题。 
一般内存池的实现思想:分配一块比较大多内存,把这块内存分成大小相等的块,即固定大小,第一块要保存必要的信息,比如nfirst(第一块可分配到块),nsize(共分配了多少),nfree(可分配块大小),pnext(若是内存池不够,分配一块growth,pnext指向下一块),p(保存第一可分配内存块的地址),同时还需要poolmanage来统一做管理。每一个内存块的头两个字节记录下一个可分配的内存块的地址,因为是固定大小的,所以可以根据p和第几块算出地址。头两个字节分配的好处就是分配之后内存可复用,注意在归还到内存池的时候头两个字节也是需要记录下一个可分配的内存块地址。 
这就是内存池的思想,好了,其实今天的主题不是内存池,而是另外一种内存管理的方法,按照块的大小来分配:

type bys_i struct {
    E *list.Element
    T int64
}

type ByteSlice struct {
    P     *BytePool
    size_ int
    ls_   *list.List
    ls_m_ map[interface{}]*bys_i
    ls_l  sync.RWMutex
    zero_ *list.Element
}
type BytePool struct {
    // Max  int64
    T    int64 //timeout when gc
    Beg  int
    End  int
    ms_  map[int]*ByteSlice
    ms_l sync.RWMutex
}

 

 

按照块的大小来维护,比如map[8]*ByteSlice,map[1024]*ByteSlice,同时,用list来做垃圾回收处理,对每个元素都设置访问时间T,若是GC时,T(当前)-T(元素)>T(GC条件),从map中delete并从list中remove。这原理跟session很相似,都是用map来存储,用list来做垃圾回收,因为map读取速度快,而list插入等操作比较灵活。 
go代码:

func NewByteSlice(p *BytePool, size int) *ByteSlice {
    ls_ := list.New()
    zero_ := ls_.PushBack([]byte{})
    return &ByteSlice{
        P:     p,
        size_: size,
        ls_:   ls_,
        zero_: zero_,
        ls_m_: map[interface{}]*bys_i{},
    }
}
func (b *ByteSlice) Alloc() []byte {
    b.ls_l.Lock()
    defer b.ls_l.Unlock()
    var bys []byte
    tv := b.ls_.Front()
    if tv == b.zero_ {
        bys = make([]byte, b.size_)
        //add
        count++
        fmt.Printf("tv==b.zero_:%v,count:%d\n", &bys[0], count)
        //end
        tv = b.ls_.PushBack(bys)
        b.ls_m_[&bys[0]] = &bys_i{
            E: tv,
            T: util.Now(),
        }
    } else {
        b.ls_.MoveToBack(tv)
        bys = tv.Value.([]byte)
        b.ls_m_[&bys[0]].T = util.Now()
        fmt.Printf("moveToBack:%v,%d,count:%d\n", &bys[0], util.Now(), count)
    }
    return bys
}
func (b *ByteSlice) Free(bys []byte) {
    b.ls_l.Lock()
    defer b.ls_l.Unlock()
    if tv, ok := b.ls_m_[&bys[0]]; ok {
        tv.T = util.Now()
        b.ls_.MoveToFront(tv.E)
    }
}
func (b *ByteSlice) Size() int64 {
    // b.ls_l.Lock()
    // defer b.ls_l.Unlock()
    return int64(b.ls_.Len()-1) * int64(b.size_)
}
func (b *ByteSlice) GC() (int, int64) {
    b.ls_l.Lock()
    defer b.ls_l.Unlock()
    tn := util.Now()
    var rc int = 0
    for {
        tv := b.ls_.Front()
        if tv == b.zero_ {
            fmt.Printf("gc tv==b.zero_\n")
            break
        }
        bys := tv.Value.([]byte)
        rv := &bys[0]
        if (tn - b.ls_m_[rv].T) > b.P.T {
            fmt.Printf("gc:%v\n", rv)
            b.ls_.Remove(tv)
            delete(b.ls_m_, rv)
            rc++
        }
    }
    return rc, b.Size()
} 



有疑问加站长微信联系

本文来自:博客园

感谢作者:williamjie

查看原文:golang 内存池

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:812540095

1208 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传