Go语言内存管理

内存分配器

线性分配器

  1. 线性分配器有较快的执行速度、较低的实现复杂度,但是无法在内存被释放时重用内存。

  2. 线性分配器需要配合使用具有拷贝特性的垃圾回收算法,如标记压缩(Mark-compact)、复制回收(Copying GC)和分代回收(Generational GC)等;通过拷贝的方式整理存活对象的碎片,将空闲内存定期合并。

空闲链表分配器

  1. 以链表的形式连接不同的内存块,所以可以重用已经释放的内存;

  2. 分配内存时需要遍历链表,从链表中选择合适的内存块,有四种分配策略:

    • 首次适应(First-Fit)— 从链表头开始遍历,选择第一个大小大于申请内存的内存块;

    • 循环首次适应(Next-Fit)— 从上次遍历的结束位置开始遍历,选择第一个大小大于申请内存的内存块;

    • 最优适应(Best-Fit)— 从链表头遍历整个链表,选择最合适的内存块;

    • 隔离适应(Segregated-Fit)— 将内存分割成多个链表,每个链表中的内存块大小相同,申请内存时先找到满足条件的链表,再从链表中选择合适的内存块,这种分配策略减少了需要遍历的内存块数量,提高了内存分配的效率,Go语言中使用的内存分配策略,与这种类似;

分级分配

  1. Go语言的内存分配器是借鉴了TCMalloc的设计,将对象根据大小分类并使用多级缓存,实施不同的分配策略;

    • 类别:微对象 (0, 16B)、小对象[16B, 32KB]、大对象(32KB, +∞)

    • 多级缓存:线程缓存(Thread Cache)、中心缓存(Central Cache)和页堆(Page Heap)

    • 线程缓存属于独立的线程,不需要互斥锁、减少了锁竞争上的性能损耗,能满足绝大多数的内存分配需求,当不能满足时会使用中心缓存解决小对象的内存分配问题,遇到32KB以上的对象时内存分配器会选择页堆直接分配内存;

      2fad4a70d28768eac488b9b00d56639b.png

虚拟内存布局

  1. Go语言1.10及以前版本使用线性的堆内存,这种设计简单且方便,但是在C和Go混合使用时会造成程序崩溃;
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容