专栏首页学而时习之go源码剖析2 内存分配1 概述
原创

go源码剖析2 内存分配1 概述

内存分配的基本策略:

  1. 每次从操作系统中分配一块大的内存(eg 1mb), 以减少系统调用;
  2. 将申请到的大块内存按照特定大小预先切分成小块, 构成链表;
  3. 为对象分配内存时, 只需要从大小合适的链表提取一小块即可;
  4. 回收对象内存时, 将该小块内存重新归还到原链表, 方便使用;
  5. 如闲置内存过低, 则尝试归还部分内存给操作系统;

注意: 内存分配器只管理内存块, 不关心内存中对象的状态, 也不会主动的回收内存, 回收是在回收器完成清理操作后, 触发内存分配器的回收操组;

内存块

  • span: 由多个连续的page组成的大块内存;
  • object: 将span按照特定大小切分成多个小块, 每个小块可存储一个对象

span面向内部管理, object面向对象分配

关于span:

  • 分配器按页数来区分不同大小的span, 以页数为单位将span存放到内存管理数组中, 需要时就以页数为索引进行查找;
  • span的大小并非固定不变, 在获取闲置span时, 如果没有找到大小合适的, 此时会引发裁剪操作, 将多余部分将构成新的span被放回管理数组;
  • 分配器会尝试将地址相邻的空闲span合并, 构建更大的内存快, 减少碎片, 提供更灵活的分配策略;

malloc.go

	pageShift = _PageShift
	_PageSize = 1 << _PageShift // 8kb

mheap.go

type mspan struct {
	next	 *mspan     // 双向链表
	prev 	 *mspan     
	start	 pageID		// 起始序号 = (address >> _pageshift)
	npages 	 uintptr	// 页数
	freelist gclinkptr  // 带分配的object 链表 
	...
	}	

关于object:

  • 用于储存对象的object, 按照8字节倍数分配N种, eg 大小为24的object 的存储范围在17-24字节的对象;
  • 虽然会造成一些内存浪费, 但是分配器只用面对有限几种规格的小内存, 优化了分配和服用的管理策略;
  • 分配器会尝试将多个微小的对象组合到一个object中, 以节约内存;

malloc.go

_MaxSamllSize = 32 << 10 //32kb
  • 若对象大小超过特定yuzhi限制, 会被当做大对象特别对待;

管理组件:

使用tcmalloc架构

优秀的内存分配器必须要在性能和内存利用率之间做到平衡. go的起点很高, 直接采用了tcmalloc架构

malloc.go

// Memory allocator.
//
// This was originally based on tcmalloc, but has diverged quite a bit.
// http://goog-perftools.sourceforge.net/doc/tcmalloc.html

分配器由三种组件组成.

  • cache:每个运行期工作线程都会绑定一个cache, 用于无锁object分配
  • central:为所有cache提供切分好的span资源
  • heap: 管理闲置span 需要时向操作向申请内存

mheap.go

type mheap strcut {
    free       [_MaxMheapList]mspan // 页数在127以内的闲置span链表数组
    freelarge  mspan                // 页数大于127(>=1MB)的大span链表
    // 每个central 对应一种 sizeclass
    central    [_NumSizeClass]struct{
                mcentral mcentral
    }
}

mcentral.go

typt mcentral strcut {
    sizeclass int32 // 规格
    nonempty  mspan // 链表, 尚有空闲的object 的span
    empty     mspan // 链表, 没有空闲的object, 或已被cache取走的span
}

mcache.go

type mcache struct {
    alloc [_NumSizeClasses]*mspan // 以sizeclass为索引管理多个用于分配的span
}

分配流程:

  1. 计算待分配对象对应的规格(size class)
  2. 从cache.alloc 数组找到规格相同的span.
  3. 从span.freelist 链表提取可用的object.
  4. if span.freelist 为空, 则去central获取新的span.
  5. if central.nonempty 为空, 从heap.free/freelarge获取, 并切分成object链表.
  6. if heap 没有大小合适的闲置span. 向操作系统申请新内存块.

# cache.alloc --> span.freelist --> central --> central.nonempty --> heap.free/freelarge

释放流程:

  1. 将标记为可回收的object交还给所属的span.freelsit
  2. 该span被放回central, 可提供cache获取新的span
  3. if span 中的object全部被回收, 则将其交还给heap. 一遍重新切分复用.
  4. 定期扫描heap中长时间闲置的span. 释放其占用的内存.

注意: 分配和回收不包括大对象, 他直接从heap分配和回收

小结

cache 是工作线程私有且不被共享, 是实现高性能无锁分配的核心.

central 的作用是在多个cache间提高object的利用率.避免内存浪费.

假设cache1 获取一个span后, 仅仅使用了一部分object, 那么剩余的空间就可能会被浪费, 而回收操作将该span交换给central后, 该span完全可以被cache2 cache3 ... 获取使用, 此时 cache1 不再持有该span, 完全不会造成问题

将span归还给heap, 是为了在不同规格object需求间平衡

某段时间内某种规格的object需求可能激增, 当需求过后, 大量的被切分成该规格的span就会闲置浪费, 将其归还给heap, 就可以被其他需求获取, 重新切分

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • go源码剖析2 内存分配2 初始化

    因为内存分期和垃圾回收算法都依赖要连续的地址, 所以在初始化阶段, 预先保留了很大的一段虚拟地址空间.

    历久尝新
  • MySQL基础篇6 mysql的行锁

    eg : 事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新

    历久尝新
  • MySQL实战问题01 索引相关

    当更新一个数据页时, 若这个数据也在内存中, 就直接更新, 但是如果这个数据页不在内存中, 在不影响数据一致性的前提下. innodb会将这些更新操作缓存在ch...

    历久尝新
  • Spark 1.6 内存管理模型( Unified Memory Management)分析

    新的内存模型是在这个Jira提出的,JIRA-10000,对应的设计文档在这:unified-memory-management。

    用户2936994
  • 为什么现在的内存分配方式可以支持递归?

    版权声明:可转载,需要明确注明转载出处和链接;不允许商业用途。 https://blog.csdn.net/bengxu/article/details...

    城市中的游牧民族
  • 疑案追踪:Spring Boot内存泄露排查记

    在项目迁移到Spring Boot之后,发生内存使用量过高的问题。本文介绍了整个排查过程以及使用到的工具,也非常适用于其他堆外内存排查。

    美团技术团队
  • 为什么现在的内存分配方式可以支持递归?

    程序员编程需要预计变量大小,指定特定内存大小给变量,无法通过代码动态给变量分配内存。程序中声明的变量在编译期间就已经被绑定到目标内存。

    城市中的游牧民族
  • DDR3内存频率标识对应

    py3study
  • flink二三事(2):起家的技术

    上一篇聊到flink的历史,请看上篇 flink两三事 ----(1)历史。 可以说基本上是起了个大早,赶了个晚集,但是flink能做今天这种热度,没有被spa...

    大数据和云计算技术
  • Spark Task 内存管理(on-heap&off-heap)

    在之前的文章中(Spark 新旧内存管理方案(上)及Spark 新旧内存管理方案(下)),我从粗粒度上对 Spark 内存管理进行了剖析,但我们依然会有类似这样...

    codingforfun

扫码关注云+社区

领取腾讯云代金券