引言
前面聊了大于8KB的内存分配,那小于8KB的呢?上一篇的平衡二叉树第十一层的叶子节点最小也是8KB,那比如要分配128B的缓存,直接分给8KB显然是不合适的,Tiny是小于512Byte,Small介于512B~8KB,Tiny和Small统称Subpage,本文就聊聊他们的内存分配情况,这块应该是整个netty最为复杂的部分了。
内容提要
下面是以分配128B为例的整体流程架构图,下面大体叙述下其流程。
先从平衡二叉树的第11层选一个未分配的叶子节点大小为8KB的一个Page
备注:本例中为memoryMap[2048]对该Page进行切割,假如要分配128B,整体会切割为64块
备注:8192/128=64通过long类型二进制64位来标记分割成各个块的分配状态
备注:0:未分配,1:已分配
一个bitmap数组长度为8,每个元素都能对64块内存进行标记
建立了二叉树节点与切分块之间的映射关系
备注:memoryMapIdx ^ maxSubpageAllocs分配后建立二叉树叶子节点与标记位之间的关系,可以指向内存一块区域
备注:0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx
源码分析
示例代码
1 |
|
备注:以分配128B的内存为例,分析其分配过程。
源码分析
1 | private long allocateSubpage(int normCapacity) { |
注解@1 从tinySubpagePools中获取PoolSubpage。获取过程为elemSize >>> 4(除以16)来获取。
tinySubpagePools结构
tinySubpagePools被初始化成长度为32的数组,元素之间差额为16B。
注解@2 allocateNode 在上一篇文章分析过,d = maxOrder = 1。表示在平衡二叉树的第11层找到可分配的节点,具体为memoryMap数组中的下标。如果整个树都没有内存可分配了,返回的id=-1。
注解@3 先看下subpages的初始化,maxSubpageAllocs = 1 << maxOrder= 2048。也就是PoolSubpage
1 | subpages = newSubpageArray(maxSubpageAllocs); |
注解@4 将平衡二叉树第11层的下标memoryMap[]的下标转换为subpages[]数组的下标。转换关系为memoryMapIdx ^ maxSubpageAllocs。
例如:平衡二叉树第11层第1个节点数组下标为2048,转换为subpages的下标为0,平衡二叉树第11层第2个节点数组下标为2049,转换为subpages的下标为1,平衡二叉树第11层第2个节点数组下标为2050,转换为subpages的下标为2。
注解@5 初始化PoolSubpage
1 | PoolSubpage(PoolSubpage<T> head, PoolChunk<T> chunk, int memoryMapIdx, int runOffset, int pageSize, int elemSize) { |
参数说明
1 | head: PoolSubpage数组中的一个元素,本例中为第4个元素 |
初始化说明
1 | void init(PoolSubpage<T> head, int elemSize) { |
过程说明
@1 先计算一个Page被切成了几份 maxNumElems( pageSize / elemSize)
@2 计算bitmap数组长度bitmapLength(maxNumElems无符号右移6位相当于除以64)
备注: 此处不太好理解为什么要maxNumElems要除以64来计算bitmap的长度呢?也就是bitmap数组中的每个元素可以标记64个被切的内存块。bitmap是long数组,每个long类型是64位,他用每个二进制位来标记被切内存块的分配情况。
加入链表
新构建的PoolSubpage与tinySubpagePools中的PoolSubpage建成链表关系。
1 | private void addToPool(PoolSubpage<T> head) { |
小结: 构造的PoolSubpage中持有了一个bitmap[]数组,数组长度与待分配的内存有关。待分配内存大小为elemSize,数组长度=PageSize/elemSize,并将bitmap数组的元素标记为未分配。
注解@6 分配内存
内存的分配以两次分配128B内存为例观察期分配过程。
1 |
|
第一次分配
第二次分配
第一次轮询第一位已被占用,需要向右移位。
第二次轮询第二位未被占用。
第二次分配过程
两次内存分配图示
第一次分配128B图示
此时64位第一位被标记为1,bitmap[0] = 1
第二次分配128B图示
此时64位第二位也被标记为1,bitmap[0] = 3