netty源码阅读之ByteBuf之内存概念arena、chunk、page、subpage

38 篇文章 1 订阅
33 篇文章 1 订阅

在PoolThreadCache里面,分两种区域,cache和arena。前面我们讲解了cache,也就是之前存在过,我们直接在cache里面获取或者分配。但是第一次的时候,cache肯定是没有的,所以就要使用到内存区域arena。

我们来看源码:

final class PoolThreadCache {

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(PoolThreadCache.class);

    final PoolArena<byte[]> heapArena;
    final PoolArena<ByteBuffer> directArena;

    // Hold the caches for the different size classes, which are tiny, small and normal.
    private final MemoryRegionCache<byte[]>[] tinySubPageHeapCaches;
    private final MemoryRegionCache<byte[]>[] smallSubPageHeapCaches;
    private final MemoryRegionCache<ByteBuffer>[] tinySubPageDirectCaches;
    private final MemoryRegionCache<ByteBuffer>[] smallSubPageDirectCaches;
    private final MemoryRegionCache<byte[]>[] normalHeapCaches;
    private final MemoryRegionCache<ByteBuffer>[] normalDirectCaches;
}

前面我们讲解了MemoryRegionCache六个cache数组,现在我们讲解heapArena和directArena。

首先我们看arena的数据结构:

 我们看到一个Arena由多个ChunkList组成,每个ChunkList之间通过双向链表的方式连接。一个ChunkList又由多个Chunk通过双向链表的方式组成。也就是各个Chunlist组成双向链表,ChunkList里面的Chunk又各自组成双向链表。

ChunList之间为什么要通过双向链表的方式连接?先给出答案:每个ChunkList里面的Chunk的使用率是不同的,Chunk使用率相同的归位一个ChunList,这样netty就能通过一定的算法找到合适的Chunk,提高内存分配的效率。

先看PoolArena里面的ChunkList的源码:

abstract class PoolArena<T> implements PoolArenaMetric {

    ...

    private final PoolChunkList<T> q050;
    private final PoolChunkList<T> q025;
    private final PoolChunkList<T> q000;
    private final PoolChunkList<T> qInit;
    private final PoolChunkList<T> q075;
    private final PoolChunkList<T> q100;

    ...

    protected PoolArena(PooledByteBufAllocator parent, int pageSize, int maxOrder, int pageShifts, int chunkSize) {

        ...

        q100 = new PoolChunkList<T>(null, 100, Integer.MAX_VALUE, chunkSize);
        q075 = new PoolChunkList<T>(q100, 75, 100, chunkSize);
        q050 = new PoolChunkList<T>(q075, 50, 100, chunkSize);
        q025 = new PoolChunkList<T>(q050, 25, 75, chunkSize);
        q000 = new PoolChunkList<T>(q025, 1, 50, chunkSize);
        qInit = new PoolChunkList<T>(q000, Integer.MIN_VALUE, 25, chunkSize);

        q100.prevList(q075);
        q075.prevList(q050);
        q050.prevList(q025);
        q025.prevList(q000);
        q000.prevList(null);
        qInit.prevList(qInit);

        ...

    }

}

通过以下代码我们可以知道,一个Arena里面有6个ChunkList:

    private final PoolChunkList<T> q050;
    private final PoolChunkList<T> q025;
    private final PoolChunkList<T> q000;
    private final PoolChunkList<T> qInit;
    private final PoolChunkList<T> q075;
    private final PoolChunkList<T> q100;

通过一下代码我么可以知道,q100里面的Chunk的内存已经使用100%以上,q075里面的Chunk的内存已经使用75%到100%之间,q050代表它里面的Chunk的内存已经使用50%到100%之间,以此类推。所以netty就可以通过这种方式找到需要的特定的内存,提高效率。

        q100 = new PoolChunkList<T>(null, 100, Integer.MAX_VALUE, chunkSize);
        q075 = new PoolChunkList<T>(q100, 75, 100, chunkSize);
        q050 = new PoolChunkList<T>(q075, 50, 100, chunkSize);
        q025 = new PoolChunkList<T>(q050, 25, 75, chunkSize);
        q000 = new PoolChunkList<T>(q025, 1, 50, chunkSize);
        qInit = new PoolChunkList<T>(q000, Integer.MIN_VALUE, 25, chunkSize);

看PoolChunkList的构造函数:


    PoolChunkList(PoolChunkList<T> nextList, int minUsage, int maxUsage, int chunkSize) {
        assert minUsage <= maxUsage;
        this.nextList = nextList;
        this.minUsage = minUsage;
        this.maxUsage = maxUsage;
        maxCapacity = calculateMaxCapacity(minUsage, chunkSize);
    }

this.nextList=nextList,结合上一段的代码,我们可以知道chunlist之间是这么连接的:

qInit->q00->q025->q050->q075->q100

然后通过下面的代码:

        q100.prevList(q075);
        q075.prevList(q050);
        q050.prevList(q025);
        q025.prevList(q000);
        q000.prevList(null);
        qInit.prevList(qInit);

我们知道最终这些Chunlist之间是这样连接的双向链表:

qInit<=>q00<=>q025<=>q050<=>q075<=>q100

 

如果我们要分配的内存小于Chunk也就是16M,我们用chunk来分配不划算,浪费空间,所以就有了page。一个page的大小是8k,如果我们要分配内存是10k,那么我们用chunk里面的两个page来分配就很划算了。但是如果只要2k呢,一个page也显得不划算,所有又有了subpage。subpage的大小介于0~8k之间。我们看到PoolArena里面有个:

    private final PoolSubpage<T>[] tinySubpagePools;
    private final PoolSubpage<T>[] smallSubpagePools;

这就是subpage,点击去:

final class PoolSubpage<T> implements PoolSubpageMetric {

    final PoolChunk<T> chunk;
    private final int memoryMapIdx;
    private final int runOffset;
    private final int pageSize;
    private final long[] bitmap;

    PoolSubpage<T> prev;
    PoolSubpage<T> next;

    boolean doNotDestroy;
    int elemSize;
    private int maxNumElems;
    private int bitmapLength;
    private int nextAvail;
    private int numAvail;
}

留意到有个elemSize,这个就是subpage的大小。可以为0~8k之间的值(当然要小于8k)

然后又个bitmap,这里先提前透露,bitmap里面如果值为1,就说明被分配了,如果为0就未分配,后面还会细讲。

然后又prev和next指针,说明subpage之间也就通过双向链表链接的。

然后第一个成员变量final PoolChunk<T> chunk,说明了这个subpage属于哪个chunk。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值