golang 内存分配

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

golang 的内存分配类似于tcmalloc(全局缓存堆和进程所属私有缓存)内存分配策略,大致采用三层内存分配模式。其中主要分为mheap, mCentral, mCache.

mheap:直接向操作系统申请内存,申请对象以页为基础单位,同时还负责大内存的直接分配。

mCentral:作为上级mheap, 下级mCache的中间项,起承上启下的作用, 当下级mCache内存不够时,mCache向mCentral申请,mCentral不够时,向mheap申请。

mCache:与MPG并发调度模型中的P关联,每个P拥有一个mCache,由于单个P每个时刻只能调度一个goroutine,所以不存在多个goroutine同时共享一个mCache,也避免了分配内存时候的加锁。mCache主要负责小对象的内存分配。

关于变量到底分配到堆上还是栈上?

​    变量的具体分配位置是由编译器决定的,当编译器无法判断变量作用域(比如返回变量地址)和变量大小(make([]int, 0, n))的时候,通常会将变量分配到堆上。理论上,编译器会优先考虑将变量分配到栈上,因为栈上的变量,会在函数出栈后自行释放,而不需要gc的处理,缓解gc压力。

关于垃圾回收?

​      go的垃圾回收使用标记-清理的方式进行内存回收,在老版本中,go从标记阶段就开始stop the world,直到清理完成后,才start the world,导致冻结所有goroutine的时间过长,目前go 在标记阶段采用了和其他协程并发的方式运行, 只有在清理的时候才会stop the world,大大缩短了阻塞时间。

关于内存优化?

​      在使用map, slice的时候,由于他们会进行动态扩容,所以在使用的时候,如果能预知分配大小,尽量初始化大小,可以避免扩容。

​      当我们在频繁使用make([]int, 0, n)的方式来分配内存的时候, 内存分配会在堆上分配,虽然mCansh已经根据size class对内部的内存进行了分类管理,但是频繁和大小不定的内存申请,必然造成内存碎片,同时,当我们再次申请时,运行时会不断的遍历内存管理链表,以求获得相应的内存块,如果内存不够,还要向上级申请内存。如此,一个简单内存分配操作,却会消耗相当长的时间,拖慢运行时间。

​      这个时候,我们就可以使用临时对象池,sync.pool,来避免频繁的分配内存,复用临时对象,同时减少了gc压力。sync.pool为什么是临时对象池呢,因为放入sync.pool中的对象,在下一次gc的时候会进行全部释放,所有put进入的对象是保证不了生命周期的。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:SuperGopher

查看原文:golang 内存分配

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

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