netty源码阅读之ByteBuf之内存subpage级别内存的分配

subpage级别的内存分配的主要方法为:allocateTiny()。

主要分为以下三个步骤:

1、定位一个subpage对象

2、初始化subpage

3、初始化pooledByteBuf

这一次,我们的用户代码是分配16B的内存

public class Test {
    public static void main(String[] args) {
        PooledByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
        allocator.directBuffer(16);
    }
}

一、定位一个subpage对象 

毫无疑问,tiny加上第一次分配不会有cache的,我们走到了这里:

我们看tinyIdx的实现:

    static int tinyIdx(int normCapacity) {
        return normCapacity >>> 4;
    }

也就是,normCapacity向右移动4位。

然后把tinySubpagePools赋给table。这个是什么意思呢?先看tinySubpagePools的构成,其实和之前MemoryRegionCache的tiny类型的组成类似,是由一个从0到496b的数组组成的。

扫描二维码关注公众号,回复: 3469733 查看本文章

tinySubpagePools:

tiny[32]={0,16B,32B,48B,...,480B,496B} 

由于tinySubpagePools是以0为首项,16为公差的等差数列。16右移4位也就是16除以16就是1,所以取tinySubpagePools的第二位,也就是下标为1,对的。

然后继续step over

我们取到table的index为tableIdx的tiny数组的节点也就是,tinySubpagePools的第二个节点,容量为16。

继续往下走,

s!=head这个条件是不满足的,我们会直接走到allocateNormal。

这里先解释一下,我们知道jdk1.7之前,hashmap的组成是数组加链表。这个tinySubpagePools也是一样的。现在index为1的位置只有一个节点,head.next还是自己,所以不走这里条件里面。继续看allocateNormal(buf,reqCapacity,normCapacity):

流程和page的是一样的,不过在c.allocate这里有所不同,进入:

这一次就进入了allocateSubpage,上一篇是allocateRun(),然后进入allocateSubpage():

从第11层开始进入,所以取maxOrder,有一段注释:

subpages are only be allocaed from pages i.e.,leaves d:11

这里就是说,subpage只能从叶子节点也就是8k为单位的节点分配,我们又拿这个图来:

也就是在8k的这些节点里面分配subpage,说得很明白: 

在memoryMap里面,第2048个节点是能够分配subpage的,验证了我们的说法。 

继续往下走:

我们刚刚说过了tinySubpagePools是数组加链表的方式,我们使用的是index为1的节点,这个节点刚开始是没有数的,所以subpage为空,所以进去新建一个:

 

我们看PoolSubpage的构造函数:

我们首先要给chunk赋值,让PoolSubpage知道自己属于哪个chunk。然后属于memoryMapIdx里面的第几个memoryMapIdx,偏移量是多少,pageSize的大小为8192也就是8k。

我们注意到elemSize=16,也就是我们要分配的内存大小,进入init:

刚刚说的elemSize在这里用上了,根据需要分配的大小,把pageSize分配为多少等分:

maxNumElems=numAvail=pageSize/elemSize;

然后它的bitmap也进行初始化,未使用就标记为0,使用就标记为1,这里是初始化,所以都是未使用,都为0。

最后有一个addToPool(head)函数:


    private void addToPool(PoolSubpage<T> head) {
        assert prev == null && next == null;
        prev = head;
        next = head.next;
        next.prev = this;
        head.next = this;
    }

也就是,把这个节点和head节点组成双向链表。

二、初始化subpage

回来这里:

进入allocate方法:

上面的那些操作就是通过一定的算法找到bitmapIdx,toHandle(bitmapIdx)这个方法很关键:

    private long toHandle(int bitmapIdx) {
        return 0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;
    }

这里的操作就是bitmapIdx左移32位,或上0x4000000000000000,最后或上memoryMapIdx。

memoryMapIdx就是chunk里面page的位置,或上0x4000000000000000的时候,在上一篇文章bitmapIdx(handle)是的时候,得到的值才能不为0。

三、初始化pooledByteBuf

最后回到这里:

进去:

刚刚说的,若是subpage的,handle为4开头的大数,bitmapIdx之后,bitmapIdx不为0,所以等下会走initBufWithSubpage这个方法:

然后继续进入:

我们看runOffset(memoryMapIdx)+(bitmapIdx&0X3FFFFFFF)*subpage.elemSize这个表达式的值:

runOffset(memoryMapIdx)因为没有偏移量,所以为0;(bitmapIdx&0X3FFFFFFF)为page的初始位置,如果现在是page里面的第1块,所以(bitmapIdx&0X3FFFFFFF)为0,如果是第2块,(bitmapIdx&0X3FFFFFFF)为1,乘上subpage.elemSize也就是16b之后,偏移量也是对的。

最后,我们就把这个流程走完了。

猜你喜欢

转载自blog.csdn.net/fst438060684/article/details/82558998