golang 内存分配

本文介绍了Go语言的内存分配策略,包括mheap、mCentral和mCache的三层模式,以及它们在内存管理中的作用。此外,讨论了变量在堆栈上的分配规则,并概述了Go的垃圾回收机制,强调了标记-清理过程的优化。最后,提出了内存优化建议,如预估map和slice的大小,以及利用sync.pool减少内存碎片和GC压力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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进入的对象是保证不了生命周期的。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值