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

怼烎@ 2022-05-14 06:51 349阅读 0赞

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

一、缓存的数据结构:

我们首先看MemoryRegionCache的定义:

  1. private abstract static class MemoryRegionCache<T> {
  2. private final int size;
  3. private final Queue<Entry<T>> queue;
  4. private final SizeClass sizeClass;
  5. private int allocations;
  6. MemoryRegionCache(int size, SizeClass sizeClass) {
  7. this.size = MathUtil.safeFindNextPositivePowerOfTwo(size);
  8. queue = PlatformDependent.newFixedMpscQueue(this.size);
  9. this.sizeClass = sizeClass;
  10. }
  11. ...
  12. }

有个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里面查看:

  1. final class PoolThreadCache {
  2. private static final InternalLogger logger = InternalLoggerFactory.getInstance(PoolThreadCache.class);
  3. final PoolArena<byte[]> heapArena;
  4. final PoolArena<ByteBuffer> directArena;
  5. // Hold the caches for the different size classes, which are tiny, small and normal.
  6. private final MemoryRegionCache<byte[]>[] tinySubPageHeapCaches;
  7. private final MemoryRegionCache<byte[]>[] smallSubPageHeapCaches;
  8. private final MemoryRegionCache<ByteBuffer>[] tinySubPageDirectCaches;
  9. private final MemoryRegionCache<ByteBuffer>[] smallSubPageDirectCaches;
  10. private final MemoryRegionCache<byte[]>[] normalHeapCaches;
  11. private final MemoryRegionCache<ByteBuffer>[] normalDirectCaches;
  12. ...
  13. }

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

  1. PoolThreadCache(PoolArena<byte[]> heapArena, PoolArena<ByteBuffer> directArena,
  2. int tinyCacheSize, int smallCacheSize, int normalCacheSize,
  3. int maxCachedBufferCapacity, int freeSweepAllocationThreshold)
  4. ...
  5. if (directArena != null) {
  6. tinySubPageDirectCaches = createSubPageCaches(
  7. tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny);
  8. ...
  9. }

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

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

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

  1. final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
  2. @Override
  3. protected synchronized PoolThreadCache initialValue() {
  4. final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
  5. final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
  6. return new PoolThreadCache(
  7. heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
  8. DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
  9. }
  10. ...
  11. }

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

  1. public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
  2. int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
  3. super(preferDirect);
  4. threadCache = new PoolThreadLocalCache();
  5. this.tinyCacheSize = tinyCacheSize;
  6. this.smallCacheSize = smallCacheSize;
  7. this.normalCacheSize = normalCacheSize;
  8. ...
  9. }

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

  1. public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
  2. this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
  3. DEFAULT_TINY_CACHE_SIZE, DEFAULT_SMALL_CACHE_SIZE, DEFAULT_NORMAL_CACHE_SIZE);
  4. }

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

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

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

然后再看PoolArena.numTinySubpagePools:

  1. static final int numTinySubpagePools = 512 >>> 4;

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

然后看SizeClass.Tiny:

  1. enum SizeClass {
  2. Tiny,
  3. Small,
  4. Normal
  5. }

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

进入那个createSubPageCaches:

  1. private static <T> MemoryRegionCache<T>[] createSubPageCaches(
  2. int cacheSize, int numCaches, SizeClass sizeClass) {
  3. if (cacheSize > 0) {
  4. @SuppressWarnings("unchecked")
  5. MemoryRegionCache<T>[] cache = new MemoryRegionCache[numCaches];
  6. for (int i = 0; i < cache.length; i++) {
  7. // TODO: maybe use cacheSize / cache.length
  8. cache[i] = new SubPageMemoryRegionCache<T>(cacheSize, sizeClass);
  9. }
  10. return cache;
  11. } else {
  12. return null;
  13. }
  14. }

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

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

  1. SubPageMemoryRegionCache(int size, SizeClass sizeClass) {
  2. super(size, sizeClass);
  3. }

调用父类构造方法:

  1. MemoryRegionCache(int size, SizeClass sizeClass) {
  2. this.size = MathUtil.safeFindNextPositivePowerOfTwo(size);
  3. queue = PlatformDependent.newFixedMpscQueue(this.size);
  4. this.sizeClass = sizeClass;
  5. }

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 条评论,349人围观)

还没有评论,来说两句吧...

相关阅读

    相关 netty阅读ByteBuf

    今天我们开启新的篇章,netty很重要的内存概念将在这一章介绍。ByteBuf主要介绍以下几点: 1、内存与内存管理器的抽象 2、不同规格大小和不同类别的内存的分配策略