netty源码阅读之ByteBuf之缓存数据结构
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都在这里了。
还没有评论,来说两句吧...