netty源码阅读之ByteBuf之缓存数据结构

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

netty分配内存的时候,有缓存和内存。我们下面两节先分析缓存的,分析缓存就要知道缓存数据结构。在这篇文章这里,我们已经知道这里有个MemoryRegionCache,一看就是缓存,那么这个MemoryRegionCache缓存的数据结构是怎么样子的呢?

一、缓存的数据结构:

我们首先看MemoryRegionCache的定义:

    private abstract static class MemoryRegionCache<T> {
        private final int size;
        private final Queue<Entry<T>> queue;
        private final SizeClass sizeClass;
        private int allocations;

        MemoryRegionCache(int size, SizeClass sizeClass) {
            this.size = MathUtil.safeFindNextPositivePowerOfTwo(size);
            queue = PlatformDependent.newFixedMpscQueue(this.size);
            this.sizeClass = sizeClass;
        }
        ...
}

有个size,这里代表的是这个MemoryRegionCache的大小,queue是维护的队列,sizeClass是规格。这么说不明白。下面解释:

1、queue

里面有handler和chunk,chunk就是内存,handler是指向内存的指针(指向唯一一块连续的内存),通过chunk和handler就可以找到唯一 一块内存。

2、sizeClass

大小有tiny(0~512B)、small(512B~8K)、normal(8K~16M),由于16M以上的数据不加入缓存,所以没有16M以上的大小;

3、size

如果是tiny的,有32个,每个的size是16B的倍数,也就是,tiny大小的MemoryRegionCache组合成一个长度为32的数组,分别有:0,16B,32B,48B,72B...496B。不包括512B。个数就是496/16+1=32.

如果是small的,有4个,每个的size分别为512B,1K,2K,4K,也就是,small大小的MemoryRegionCache组合成一个长度为4的数组。

如果是normal的,有3个,每个的size分别为8K,16K,32K,也就是normal大小的MemoryRegionCache组合成一个长度为3的数组。

所以有32+4+3=39种内存规格的MemoryRegionCache。

大概这样,不明白的让我们继续从源码里面去看。

 

MemoryRegionCache属于缓存,属于每个线程私有的缓存,属于每个线程都会维护的对象,所以我们在PoolThreadCache里面查看:

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;
    ...
}

我们可以看到PoolThreadCache里面有6种规格的缓存。因为我们本来有tiny、small、normal3种规格的缓存,这3种里面 又有direct和heap,所以一共有6种,并且他们都是数组。我们首先看tinySubPageDirectCaches把,其他的一样分析,找到它的赋值的地方:

    PoolThreadCache(PoolArena<byte[]> heapArena, PoolArena<ByteBuffer> directArena,
                    int tinyCacheSize, int smallCacheSize, int normalCacheSize,
                    int maxCachedBufferCapacity, int freeSweepAllocationThreshold)
            ...
        if (directArena != null) {
            tinySubPageDirectCaches = createSubPageCaches(
                    tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);
            ...
        
    }

这是在PoolThreadCache初始化的时候,创建这个缓存。

我们一步步追踪每一个参数,首先是tinyCacheSize:

由PoolThreadCache来的,我们知道是在PoolThreadLocalCache的initialValue方法创建这个PoolThreadCache的,这篇文章可以知道。

    final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {

        @Override
        protected synchronized PoolThreadCache initialValue() {
            final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
            final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);

            return new PoolThreadCache(
                    heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
                    DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
        }
        ...
}

而PoolThreadLocalCache的tinyCacheSize又是从PooledByteBufAllocator的成员变量来的,这个成员变量是在这个构造方法里面赋值的:

    public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
                                  int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
        super(preferDirect);
        threadCache = new PoolThreadLocalCache();
        this.tinyCacheSize = tinyCacheSize;
        this.smallCacheSize = smallCacheSize;
        this.normalCacheSize = normalCacheSize;
        ...
}

而这个构造方法从这里来:

   public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
        this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
                DEFAULT_TINY_CACHE_SIZE, DEFAULT_SMALL_CACHE_SIZE, DEFAULT_NORMAL_CACHE_SIZE);
    }

就是这个DEFAULT_TINY_CACHE_SIZE,看看它的赋值:

        // cache sizes
        DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);
        DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
        DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);

tiny默认的是512的大小,small的默认是256的大小,normal默认是64的大小。

 

然后再看PoolArena.numTinySubpagePools:

    static final int numTinySubpagePools = 512 >>> 4;

也就是512/16=32,也就是tiny的默认是32个。

 

然后看SizeClass.Tiny:

    enum SizeClass {
        Tiny,
        Small,
        Normal
    }

有三种规格,我们前面也介绍过。

 

进入那个createSubPageCaches:

    private static <T> MemoryRegionCache<T>[] createSubPageCaches(
            int cacheSize, int numCaches, SizeClass sizeClass) {
        if (cacheSize > 0) {
            @SuppressWarnings("unchecked")
            MemoryRegionCache<T>[] cache = new MemoryRegionCache[numCaches];
            for (int i = 0; i < cache.length; i++) {
                // TODO: maybe use cacheSize / cache.length
                cache[i] = new SubPageMemoryRegionCache<T>(cacheSize, sizeClass);
            }
            return cache;
        } else {
            return null;
        }
    }

现在cachesize是512,numCaches是32,sizeClass是tiny。

所以创建一个32长度的MemoryRegionCache,SubPageMemoryRegionCache继承自MemoryRegionCache。看看SubPageMemoryRegionCache的创建过程:

        SubPageMemoryRegionCache(int size, SizeClass sizeClass) {
            super(size, sizeClass);
        }

调用父类构造方法:

        MemoryRegionCache(int size, SizeClass sizeClass) {
            this.size = MathUtil.safeFindNextPositivePowerOfTwo(size);
            queue = PlatformDependent.newFixedMpscQueue(this.size);
            this.sizeClass = sizeClass;
        }

size是512,sizeClass是tiny,

找到下一个2的n次方的数,这里还是512(可以看源码的实现)

 this.size = MathUtil.safeFindNextPositivePowerOfTwo(size)

创建一个队列,容量大小为size(也就是队列的大小):

queue = PlatformDependent.newFixedMpscQueue(this.size);

最后把sizeClass赋值给它:

this.sizeClass = sizeClass;

 

所以我们上面所说的数据结构,size,queue和sizeClass都在这里了。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值