Go语言设计与实现之内存分配器
文章目录
go内存分配器
程序中的数据和变量都会被分配到程序所在的虚拟内存中,内存空间包含两个重要区域:栈区(Stack)和堆区(Heap)。函数调用的参数、返回值以及局部变量大都会被分配到栈上(可能发生内存逃逸),这部分内存会由编译器进行管理;不同编程语言使用不同的方法管理堆区的内存,C++ 等编程语言会由工程师主动申请和释放内存,Go 以及 Java 等编程语言会由工程师和编译器共同管理,堆中的对象由内存分配器分配并由垃圾收集器回收。
内存分配方法
-
线性分配法
只在内存中维护一个指针,并指向剩余空闲内存地址,如果分配新的内存空间只需要将指针往后移即可,如果释放内存,需要通过额外操作来整理内存碎片。(线性分配器本身是无法合理利用已经被释放的内存。)
-
空闲链表分配法
空闲内存块之间通过指针相连,相比于线性分配法,可以重用已经被释放的内存。
-
go的内存分配策略:隔离适应策略
对不同大小的内存块分级,减少内存块链表的长度。分配时,先根据内存大小直接定位到特定的内存块链表头节点,然后在链表里查找空闲的内存块。(类似于二分的思想,可以减少遍历内存块的数量,提升效率)
多级缓存
内存分配会根据所需内存的大小,从不同级别缓存申请内存。
-
线程缓存
线程缓存属于每一个独立的线程内,并没有并发安全问题,申请很快。
-
中心缓存
中心缓存可能涉及到多个线程并发申请,需要加锁访问。
-
页堆
前面两种无法满足内存申请,则需要从页堆申请新的内存空间。
分级分配
go划分了对象大小的级别,按照对象的不同大小,采用不同的分配策略。
-
微对象(0, 16B]
先使用微型分配器,再依次尝试线程缓存、中心缓存和堆分配内存;
-
小对象(16B, 32KB]
依次尝试使用线程缓存、中心缓存和堆分配内存;
-
大对象(32KB, +♾️)
大于32KB的内存分配申请,会直接在堆上申请。
go的虚拟内存布局
暂略
文章作者 cold-bin
上次更新 2023-10-29